I started to learn programming in Go and came up with a Breakable Toy, which is a CLI for Gitlab. To get something up and running fast, I used some Go libraries:
- Ginkgo for testing
- Cobra for basic CLI stuff
- Sling for HTTP requests and JSON mapping
The project can be found in this repository on Github.
I am looking for any feedback / code review that could help me to better understand the concepts and conventions of Go. I come from the Java world and have the impression that I try to stick too much on the object oriented world. So any feedback concerning code style and best-practices is highly appreciated.
Besides general feedback, here are some specific questions:
1. Error Handling in Go
Being used to the try catch
mechanism in Java, the _, err :=
mechanism in Go seems a little unhandy to me. To me it looks as if I have to implement the "bubble up" of exceptions all by myself. Example:
// HTTP client
func (client *GitlabClient) Do(req *http.Request, value interface{}) (*http.Response, error) {
resp, err := client.sling.Do(req, value, nil)
return resp, err
}
// service
func (service *ProjectsService) List() (*[]model.Project, error) {
// ...
_, err = service.Client.Do(req, projects)
if err != nil {
return nil, err
}
return projects, nil
}
// CLI command
func(cmd *cobra.Command, args []string) error {
projects, err := gitlabClient.Projects.List()
if err != nil {
return err
}
err = OutputJson(projects)
return err
}
so there are really many LOCs only for error handling - is that the "way to go"?
2. Global Variables
The Cobra library seems to store values for CLI flags in global variables:
var id string
// ...
projectGetCmd.PersistentFlags().StringVarP(&id, "id", "i", "", "(required) Either ID of project or 'namespace/project-name'")
viper.BindPFlag("id", projectGetCmd.PersistentFlags().Lookup("id"))
This makes it really hard to figure out which flags belong to which command and it's easy to re-initialize the same variable. See https://github.com/michaellihs/golab/blob/master/cmd/group.go#L31 for the full example.
Is there any rule of thumb / best practice of how to namespace variables in Go besides structs?
3. Mocking in Unit Tests
I wrote some integration tests with Ginkgo that also simulate an HTTP server. This works quite nicely. But now I wanted to test, whether my CLI commands are calling the expected service methods and therefore I wanted to mock the services classes in the Unit tests for the commands. I couldn't find a convincing way for how to mock objects in Go. Can you give me any hint / resources for that?
Disclaimer
I am not expecting a full code review of my application. But if you have any suggestions for improvement, I'd be glad the get them.