Imagine there is a User package which contains only two simple methods
Hello which says "Hello"
Say which implements how a user speaks
Original
package user
import "fmt"
type user struct {}
func (u user) Hello() {
u.Say("Hello")
}
func (u user) Say(sentence string) {
fmt.Println(sentence)
}
However, we are not able to unit test Hello since it depends on Say which is not mockable.
After searing on StackOverflow and Goole, I summarize two methods to solve the problem, but none of them are perfect.
Method 1 - Use lambda func
user.go
package user
import "fmt"
type user struct{}
func (u user) Hello() {
say("Hello")
}
func (u user) Say(sentence string) {
say(sentence)
}
var say = func(sentence string) {
fmt.Println(sentence)
}
user_test.go
package user
import (
"testing"
)
func TestHello(t *testing.T) {
sayCalled := 0
sayCallArg := ""
mockSay := func(sentence string) {
sayCalled++
sayCallArg = sentence
}
say = mockSay
u := user{}
u.Hello()
if sayCalled != 1 {
t.Fatalf("not called")
}
if sayCallArg != "Hello" {
t.Fatalf("wrong arg")
}
}
Method 2 - Use interface
user.go
package user
import "fmt"
type user struct {
sayer Sayer
}
func (u user) Hello() {
u.sayer.Say("Hello")
}
func (u user) Say(sentence string) {
u.sayer.Say(sentence)
}
type Sayer interface {
Say(string)
}
type sayer struct{}
func (s sayer) Say(sentence string) {
fmt.Println(sentence)
}
user_test.go
package user
import (
"testing"
)
type mockSayer struct {
called int
calledArg string
}
func (s *mockSayer) Say(sentence string) {
s.called++
s.calledArg = sentence
}
func TestHello(t *testing.T) {
mockSayer := &mockSayer{}
u := user{sayer: mockSayer}
u.Hello()
if mockSayer.called != 1 {
t.Fatalf("not called")
}
if mockSayer.calledArg != "Hello" {
t.Fatalf("wrong arg")
}
}
I understand most of the cases, people will suggest to use Method 2 since that's how dependency injection works in Go.
However, in this example, it's weird to extract the implementation of Say to another layer (unnecessary complexity in my opinion).
Is there any better solution to solve this kind of dependency?
or which method you prefer and why?
None of the above. I don't see where you prove that the Hello method actually works, that "Hello\n" is actually written. Check the Say method output. Mock os.Stdout. For example,
user.go:
package user
import (
"fmt"
"io"
"os"
)
type user struct{}
const hello = "Hello"
func (u user) Hello() {
u.Say(hello)
}
var stdwrite = io.Writer(os.Stdout)
func (u user) Say(sentence string) {
fmt.Fprintln(stdwrite, sentence)
}
user_test.go:
package user
import (
"bytes"
"io"
"testing"
)
func TestHello(t *testing.T) {
u := user{}
u.Hello() // for real
defer func(w io.Writer) { stdwrite = w }(stdwrite)
stdwrite = new(bytes.Buffer)
u.Hello() // for test
got := stdwrite.(*bytes.Buffer).String()
want := hello + "\n"
if got != want {
t.Errorf("want: %q got: %q", want, got)
}
}
Output:
$ go test -v
=== RUN TestHello
Hello
--- PASS: TestHello (0.00s)
PASS
ok say 0.001s
Related
in this case I am using
Echo
mongo-driver (MongoDB)
gqlgen graphql type safe
is there any simple example for testing my Mutation and Query ? I do love to get advices for this. I have tried to find but can't found any so far,
thank you :)
Here is an test example using testify package and the gqlgen/client package which is used internally for testing.
GraphQL schema:
type Query {
user(loginname: String!): UserDetail
}
type Mutation {
validateAccessToken(accesstoken: String!): UserEntity
}
type User {
loginname: String
avatarUrl: String
}
type UserEntity {
id: ID!
loginname: String
avatarUrl: String
}
type UserDetail {
loginname: String
avatarUrl: String
githubUsername: String
createAt: String
score: Int
}
graph/resolver/resolver.go:
package resolver
import "github.com/mrdulin/gqlgen-cnode/services"
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
UserService services.UserService
}
services/user.go:
package services
type UserService interface {
GetUserByLoginname(loginname string) *model.UserDetail
ValidateAccessToken(accesstoken string) *model.UserEntity
}
graph/resolver/root.resolver.go:
package resolver
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
import (
"context"
"github.com/mrdulin/gqlgen-cnode/graph/generated"
"github.com/mrdulin/gqlgen-cnode/graph/model"
)
func (r *mutationResolver) ValidateAccessToken(ctx context.Context, accesstoken string) (*model.UserEntity, error) {
return r.UserService.ValidateAccessToken(accesstoken), nil
}
func (r *queryResolver) User(ctx context.Context, loginname string) (*model.UserDetail, error) {
return r.UserService.GetUserByLoginname(loginname), nil
}
// Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
Created mock object for UserService:
mocks/userService.go:
package mocks
import (
"github.com/mrdulin/gqlgen-cnode/graph/model"
"github.com/stretchr/testify/mock"
)
type MockedUserService struct {
mock.Mock
}
func (s *MockedUserService) GetUserByLoginname(loginname string) *model.UserDetail {
args := s.Called(loginname)
return args.Get(0).(*model.UserDetail)
}
func (s *MockedUserService) ValidateAccessToken(accesstoken string) *model.UserEntity {
args := s.Called(accesstoken)
return args.Get(0).(*model.UserEntity)
}
graph/resolver/root.resolver_test.go:
package resolver_test
import (
"testing"
"github.com/99designs/gqlgen/client"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/mrdulin/gqlgen-cnode/graph/generated"
"github.com/mrdulin/gqlgen-cnode/graph/model"
"github.com/mrdulin/gqlgen-cnode/graph/resolver"
"github.com/mrdulin/gqlgen-cnode/mocks"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var (
loginname = "mrdulin"
avatarURL = "avatar.jpg"
score = 50
createAt = "1900-01-01"
)
func TestMutationResolver_ValidateAccessToken(t *testing.T) {
t.Run("should validate accesstoken correctly", func(t *testing.T) {
testUserService := new(mocks.MockedUserService)
resolvers := resolver.Resolver{UserService: testUserService}
c := client.New(handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers})))
ue := model.UserEntity{ID: "123", User: model.User{Loginname: &loginname, AvatarURL: &avatarURL}}
testUserService.On("ValidateAccessToken", mock.AnythingOfType("string")).Return(&ue)
var resp struct {
ValidateAccessToken struct{ ID, Loginname, AvatarUrl string }
}
q := `
mutation {
validateAccessToken(accesstoken: "abc") {
id,
loginname,
avatarUrl
}
}
`
c.MustPost(q, &resp)
testUserService.AssertExpectations(t)
require.Equal(t, "123", resp.ValidateAccessToken.ID)
require.Equal(t, "mrdulin", resp.ValidateAccessToken.Loginname)
require.Equal(t, "avatar.jpg", resp.ValidateAccessToken.AvatarUrl)
})
}
func TestQueryResolver_User(t *testing.T) {
t.Run("should query user correctly", func(t *testing.T) {
testUserService := new(mocks.MockedUserService)
resolvers := resolver.Resolver{UserService: testUserService}
c := client.New(handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers})))
u := model.UserDetail{User: model.User{Loginname: &loginname, AvatarURL: &avatarURL}, Score: &score, CreateAt: &createAt}
testUserService.On("GetUserByLoginname", mock.AnythingOfType("string")).Return(&u)
var resp struct {
User struct {
Loginname, AvatarURL, CreateAt string
Score int
}
}
q := `
query GetUser($loginname: String!) {
user(loginname: $loginname) {
loginname
avatarUrl
createAt
score
}
}
`
c.MustPost(q, &resp, client.Var("loginname", "mrdulin"))
testUserService.AssertCalled(t, "GetUserByLoginname", "mrdulin")
require.Equal(t, "mrdulin", resp.User.Loginname)
require.Equal(t, "avatar.jpg", resp.User.AvatarURL)
require.Equal(t, 50, resp.User.Score)
require.Equal(t, "1900-01-01", resp.User.CreateAt)
})
}
test result:
=== RUN TestMutationResolver_ValidateAccessToken
=== RUN TestMutationResolver_ValidateAccessToken/should_validate_accesstoken_correctly
TestMutationResolver_ValidateAccessToken/should_validate_accesstoken_correctly: root.resolvers_test.go:44: PASS: ValidateAccessToken(mock.AnythingOfTypeArgument)
--- PASS: TestMutationResolver_ValidateAccessToken (0.00s)
--- PASS: TestMutationResolver_ValidateAccessToken/should_validate_accesstoken_correctly (0.00s)
=== RUN TestQueryResolver_User
=== RUN TestQueryResolver_User/should_query_user_correctly
--- PASS: TestQueryResolver_User (0.00s)
--- PASS: TestQueryResolver_User/should_query_user_correctly (0.00s)
PASS
ok github.com/mrdulin/gqlgen-cnode/graph/resolver 0.141s
test coverage report:
source code: https://github.com/mrdulin/gqlgen-cnode
I want to test some CLI routines in go that are run in the main() function purely, they are just exercises that I'm doing, but I want to make tests on them!
So for example how can I pass arguments to table test this king of algorithm?
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
var f *os.File
f = os.Stdin
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if scanner.Text() == "STOP" || scanner.Text() == "stop" {
break
}
n, err := strconv.ParseInt(scanner.Text(), 10, 64)
if err == nil {
fmt.Printf("Number formatted: %d\n", n)
} else {
fmt.Println(err.Error())
}
}
}
I put the code on playground too for better help!
https://play.golang.org/p/JgrQ2yFogNs
Thanks in advance!
You need to create a function which input and output channels as params. It should read and write to these params. Following is an example:
main.go
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
)
func main() {
var f *os.File
f = os.Stdin
defer f.Close()
run (os.Stdin, f)
}
func run(in io.Reader, out io.Writer) {
scanner := bufio.NewScanner(in)
for scanner.Scan() {
if scanner.Text() == "STOP" || scanner.Text() == "stop" {
break
}
n, err := strconv.ParseInt(scanner.Text(), 10, 64)
if err == nil {
fmt.Printf("Number formatted: %d\n", n)
} else {
fmt.Println(err.Error())
}
}
}
main_test.go
package main
import (
"bytes"
"fmt"
"testing"
)
func TestRun(t *testing.T){
var command, result bytes.Buffer
fmt.Fprintf(&command, "10\n")
fmt.Fprintf(&command, "stop\n")
run(&command, &result)
got := result.String()
//test for contents of "got"
fmt.Println(got)
}
Now you can run the following on a command line.
go test
Strictly speaking, you don't need to use Go for this. When I'm looking at writing a CLI tool I break out of main as soon as possible with functions and structs the unit test those in the usual way.
To make sure that all the CLI arguments and file system buts are plumbed in correctly I've been using https://github.com/bats-core/bats-core to run the command and check the fully built tool.
You can see an example of how I've done this at https://github.com/dnnrly/abbreviate
Perhaps not the answer you're looking for but it's been working well for me.
I use the following code which works ok.
This is working example
https://play.golang.org/p/wjvJtDNvJAQ
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type requester interface {
HTTPRequest(c string, i string, mtd string, url string) (p []byte, e error)
}
type impl struct {
client *http.Client
}
// ----This is the function which I need to mock
func (s *ServiceInfo) wrapperFN() {
// Function 1 - get the values
v1, v2 := s.json.parseJson()
// call to http function
s.req.HTTPRequest(v1, v2, "POST", "http://www.mocky.io/v2/5c20eccc2e00005c001e0c84")
}
func (i impl) HTTPRequest(c string, ci string, mtd string, url string) (p []byte, e error) {
req, err := http.NewRequest(mtd, url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c, ci)
res, err := i.client.Do(req)
if err != nil {
return nil, err
}
token, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
defer res.Body.Close()
fmt.Println("success")
return token, nil
}
type parser interface {
parseJson() (string, string)
}
type jsonP struct {
data string
}
func (s jsonP) parseJson() (string, string) {
var result map[string]interface{}
json.Unmarshal([]byte(s.data), &result)
b := result["person"].(map[string]interface{})
for key, value := range b {
return key, value.(string)
}
return "", ""
}
type ServiceInfo struct {
req requester
json parser
}
// When in production pass in concrete implementations.
func NewServiceInfo(http requester, json parser) *ServiceInfo {
return &ServiceInfo{
req: http,
json: json,
}
}
func main() {
httpClient := http.Client{}
js := `{"person":{"p1":"username","p2":"password"},"customers":"10"}`
j := jsonP{data: js}
s := NewServiceInfo(impl{client: &httpClient}, j)
s.wrapperFN()
}
Now i want to test it wrapperFN , what I try I've changed the code to use interface , which works.
This is just example to give a point ( the real code much more complicated)
The problem that I dont understand how to mock function inside wrapperFN like parseJson() , in the real world warpperFN contains several function which I need to mock ,because just calling them in the test will provide error.
How it's best to mock function like parseJson() & HTTPRequest? and assume that inside wrapperFN there is additional functions which is not related...
I need to know if this is the best practice for testing function.
This is the test (which im not sure how to make it right)
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestServiceInfo_wrapperFN(t *testing.T) {
tests := []struct {
name string
s *ServiceInfo
}{
{
name: "wrapper test",
s: &ServiceInfo{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var testHandler http.Handler
srv := httptest.NewServer(testHandler)
defer srv.Close()
iReq := &impl{
client: srv.Client(),
}
v := &ServiceInfo{http: *iReq}
v.wrapperFN()
})
}
}
I have next struct.
package logger
import "fmt"
type IPrinter interface {
Print(value string)
}
type ConsolePrinter struct{}
func (cp *ConsolePrinter) Print(value string) {
fmt.Printf("this is value: %s", value)
}
Test coverage says I need to test that ConsolePrinter Print method.
How can I cover this method?
Thanks.
Following comment that #icza wrote, I've written test below.
func TestPrint(t *testing.T) {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
cp := &ConsolePrinter{}
cp.Print("test")
w.Close()
out, _ := ioutil.ReadAll(r)
os.Stdout = rescueStdout
if string(out) != "this is value: test" {
t.Errorf("Expected %s, got %s", "this is value: test", out)
}
}
I've found example in question What is the best way to convert byte array to string?.
Use Examples to convey the usage of a function.
Don't fret over 100% test coverage, especially for simple straightforward functions.
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The additional benefit is that examples are outputted in a generated doc with go doc tool.
I would recommend to create new instance of the logger, which would behave exactly as fmt methods for printing data to console. Also, you can configure it with additional features like showing the filename, date and etc. Such custom logger can be passed as a parameter to your service/instance factory method. That would make it really easy to mock and test.
Your code
type Logs interface {
Println(v ...interface{})
}
type InstanceToTest struct {
log Logs
}
func InstanceToTestFactory(logger Logs) *InstanceToTest {
return &InstanceToTest{logger}
}
func (i *InstanceToTest) SomeMethod(a string) {
i.log.Println(a)
}
Create a mock for logger
type LoggerMock struct {
CalledPrintln []interface{}
}
func (l *LoggerMock) Println(v ...interface{}) {
l.CalledPrintln = append(CalledPrintln, v)
}
And in your test
func TestInstanceToTestSomeMethod(t *testing.T) {
l := &LoggerMock{}
i := InstanceToTestFactory(l)
a := "Test"
i.SomeMethod(a)
if len(l.CalledPrintln) == 0 || l.CalledPrintln[0] != a {
t.Error("Not called")
}
}
I'm writing a REST API using Gin framework. But I was faced a trouble testing my controllers and researching TDD and Mock. I tried to apply TDD and Mock to my code but I could not.
I created a very reduced test environment and tried to create a controller test. How do I create a Mock for Gin.Context?
Here's my example code:
package main
import (
"strconv"
"github.com/gin-gonic/gin"
)
// MODELS
type Users []User
type User struct {
Name string `json"name"`
}
func main() {
r := gin.Default()
r.GET("/users", GetUsers)
r.GET("/users/:id", GetUser)
r.Run(":8080")
}
// ROUTES
func GetUsers(c *gin.Context) {
repo := UserRepository{}
ctrl := UserController{}
ctrl.GetAll(c, repo)
}
func GetUser(c *gin.Context) {
repo := UserRepository{}
ctrl := UserController{}
ctrl.Get(c, repo)
}
// CONTROLLER
type UserController struct{}
func (ctrl UserController) GetAll(c *gin.Context, repository UserRepositoryIterface) {
c.JSON(200, repository.GetAll())
}
func (ctrl UserController) Get(c *gin.Context, repository UserRepositoryIterface) {
id := c.Param("id")
idConv, _ := strconv.Atoi(id)
c.JSON(200, repository.Get(idConv))
}
// REPOSITORY
type UserRepository struct{}
type UserRepositoryIterface interface {
GetAll() Users
Get(id int) User
}
func (r UserRepository) GetAll() Users {
users := Users{
{Name : "Wilson"},
{Name : "Panda"},
}
return users
}
func (r UserRepository) Get(id int) User {
users := Users{
{Name : "Wilson"},
{Name : "Panda"},
}
return users[id-1]
}
My test example:
package main
import(
"testing"
_ "github.com/gin-gonic/gin"
)
type UserRepositoryMock struct{}
func (r UserRepositoryMock) GetAll() Users {
users := Users{
{Name : "Wilson"},
{Name : "Panda"},
}
return users
}
func (r UserRepositoryMock) Get(id int) User {
users := Users{
{Name : "Wilson"},
{Name : "Panda"},
}
return users[id-1]
}
// TESTING REPOSITORY FUNCTIONS
func TestRepoGetAll(t *testing.T) {
userRepo := UserRepository{}
amountUsers := len(userRepo.GetAll())
if amountUsers != 2 {
t.Errorf("Esperado %d, recebido %d", 2, amountUsers)
}
}
func TestRepoGet(t *testing.T) {
expectedUser := struct{
Name string
}{
"Wilson",
}
userRepo := UserRepository{}
user := userRepo.Get(1)
if user.Name != expectedUser.Name {
t.Errorf("Esperado %s, recebido %s", expectedUser.Name, user.Name)
}
}
/* HOW TO TEST CONTROLLER?
func TestControllerGetAll(t *testing.T) {
gin.SetMode(gin.TestMode)
c := &gin.Context{}
c.Status(200)
repo := UserRepositoryMock{}
ctrl := UserController{}
ctrl.GetAll(c, repo)
}
*/
Gin provides the option to create a Test Context which you can use for whatever you need:
https://godoc.org/github.com/gin-gonic/gin#CreateTestContext
Like that:
c, _ := gin.CreateTestContext(httptest.NewRecorder())
Here is an example of how I mock a context, add a param, use it in a function, then print the string of the response if there was a non-200 response.
gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = []gin.Param{gin.Param{Key: "k", Value: "v"}}
foo(c)
if w.Code != 200 {
b, _ := ioutil.ReadAll(w.Body)
t.Error(w.Code, string(b))
}
In order to get a *gin.Context instance that you can test, you need a mock HTTP request and response. An easy way to create those is to use the net/http and net/http/httptest packages. Based on the code you linked, your test would look like this:
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestControllerGetAll(t *testing.T) {
// Switch to test mode so you don't get such noisy output
gin.SetMode(gin.TestMode)
// Setup your router, just like you did in your main function, and
// register your routes
r := gin.Default()
r.GET("/users", GetUsers)
// Create the mock request you'd like to test. Make sure the second argument
// here is the same as one of the routes you defined in the router setup
// block!
req, err := http.NewRequest(http.MethodGet, "/users", nil)
if err != nil {
t.Fatalf("Couldn't create request: %v\n", err)
}
// Create a response recorder so you can inspect the response
w := httptest.NewRecorder()
// Perform the request
r.ServeHTTP(w, req)
// Check to see if the response was what you expected
if w.Code != http.StatusOK {
t.Fatalf("Expected to get status %d but instead got %d\n", http.StatusOK, w.Code)
}
}
Although you could create a mock *gin.Context, it's probably easier to use the method above, since it'll execute and handle your request the same as it would an actual request.
If to reduce the question to "How to create mock for a function argument?" the answer is: use interfaces not concrete types.
type Context struct is a concrete type literal and Gin doesn't provide appropriate interface. But you can declare it by yourself. Since you are using only JSON method from Context you can declare extra-simple interface:
type JSONer interface {
JSON(code int, obj interface{})
}
And use JSONer type instead Context type in all your functions which expect Context as argument:
/* Note, you can't declare argument as a pointer to interface type,
but when you call it you can pass pointer to type which
implements the interface.*/
func GetUsers(c JSONer) {
repo := UserRepository{}
ctrl := UserController{}
ctrl.GetAll(c, repo)
}
func GetUser(c JSONer) {
repo := UserRepository{}
ctrl := UserController{}
ctrl.Get(c, repo)
}
func (ctrl UserController) GetAll(c JSONer, repository UserRepositoryIterface) {
c.JSON(200, repository.GetAll())
}
func (ctrl UserController) Get(c JSONer, repository UserRepositoryIterface) {
id := c.Param("id")
idConv, _ := strconv.Atoi(id)
c.JSON(200, repository.Get(idConv))
}
And now it is easy to test
type ContextMock struct {
JSONCalled bool
}
func (c *ContextMock) JSON(code int, obj interface{}){
c.JSONCalled = true
}
func TestControllerGetAll(t *testing.T) {
gin.SetMode(gin.TestMode)
c := &ContextMock{false}
c.Status(200)
repo := UserRepositoryMock{}
ctrl := UserController{}
ctrl.GetAll(c, repo)
if c.JSONCalled == false {
t.Fail()
}
}
Example simple as possible.
There is another question with a close sense