Home  >  Article  >  Backend Development  >  Assertion: Mock: I don't know what to return because the method call was unexpected Error while writing unit test in Go

Assertion: Mock: I don't know what to return because the method call was unexpected Error while writing unit test in Go

王林
王林forward
2024-02-10 09:00:18493browse

断言:模拟:我不知道要返回什么,因为方法调用是意外的 在 Go 中编写单元测试时出错

In this article, php editor Xiaoxin will introduce you to a common error that occurs when writing unit tests in the Go language, that is, assertion errors. When we write unit tests, we sometimes encounter situations where the return value cannot be determined, which can lead to unexpected method call errors. In this article, we will discuss the causes and solutions to this problem to help you better handle assertion errors and ensure the accuracy and reliability of your unit tests.

Question content

I use testify in go to write unit tests for my service methods. All methods work fine except the update method, because during the update In the method, I call another method of the same service ("getbyid") in the update method.

Implement the update method in my service:

func (ts *teamservice) update(team *team.team) apperror {
    t, err := ts.teamrepo.getbyid(team.id)
    if err != nil {
        return err
    }
    if t.teamownerid != team.teamownerid {
        return newforbiddenerror(forbiddenerr)
    }

    return ts.teamrepo.update(team)
}

mockrepo update method:

func (t *teamrepomock) update(team *team.team) apperror {
    args := t.called(team)
    if args.error(0) != nil {
        return newnotfounderror(args.error(0))
    }

    return nil
}

Test implementation:

func testupdate(t *testing.t) {
    _, teamidgen, playeridgen := setupconfig()

    t.run("update a team", func(t *testing.t) {
        teamrepo, _, ts := setupteamservice(teamidgen, playeridgen)

        teamrepo.on("update", testteam1).return(nil)
        result := ts.update(testteam1)

        assert.nil(t, result)
    })

    t.run("update a team fails", func(t *testing.t) {
        teamrepo, _, ts := setupteamservice(teamidgen, playeridgen)

        expected := oopserr
        teamrepo.on("update", testteam1).return(expected)
        result := ts.update(testteam1)

        assert.equalvalues(t, expected.error(), result.error())
    })
}

Now when I run the test I get the following error:

--- FAIL: TestUpdate (0.01s)
    --- FAIL: TestUpdate/Update_a_team (0.01s)
panic: 
assert: mock: I don't know what to return because the method call was unexpected.
    Either do Mock.On("GetByID").Return(...) first, or remove the GetByID() call.
    This method was unexpected:
        GetByID(string)
        0: ""
    at: [/home/waleem/Desktop/project/eazykhel_server/services/teamservice/team_service_init_test.go:18 /home/waleem/Desktop/project/eazykhel_server/services/teamservice/team_service.go:146 /home/waleem/Desktop/project/eazykhel_server/services/teamservice/team_service_test.go:277] [recovered]
    panic:

I tried calling .on("update") before and after calling mock.on("getbyid") in the test function implementation but it doesn't work and I also modified the mockrepo update function but it didn't work.

Solution

Let me try to help you solve your problem. I copied the repository with some simplifications just to post the relevant code. If I'm not wrong in your solution, there is a service (teamservice) that calls some methods provided by the underlying package (teamrepo). You want to test the update method of the teamservice structure. After reviewing, let me show the code first and then I will try to explain each file:

repo/repo.go

package repo

type team struct {
    id          int
    teamownerid int
    name        string
}

type teamrepo struct{}

func (t *teamrepo) getbyid(id int) (team, error) {
    return team{id: id, teamownerid: id, name: "myteam"}, nil
}

func (t *teamrepo) update(team team) error {
    return nil
}

In this file we can find the method to simulate. The methods are: getbyid and update. Obviously this isn't your actual code, but that doesn't matter right now.

services/service.go

package services

import (
    "errors"

    "testifymock/repo"
)

type teamservice struct {
    tr teamrepointerface
}

func newteamservice(repo teamrepointerface) *teamservice {
    return &teamservice{
        tr: repo,
    }
}

type teamrepointerface interface {
    getbyid(id int) (repo.team, error)
    update(team repo.team) error
}

func (ts *teamservice) update(team *repo.team) error {
    t, err := ts.tr.getbyid(team.id)
    if err != nil {
        return err
    }

    if t.teamownerid != team.teamownerid {
        return errors.new("forbidden")
    }

    return ts.tr.update(*team)
}

Here we can see in the test code the service that will become our system under test (sut). With dependency injection, we will take advantage of the functionality provided by the repo package injected through the interface teamrepointerface.

services/service_test.go

package services

import (
    "errors"
    "testing"

    "testifymock/repo"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

// 1. declare the mock struct
type teamRepoMock struct {
    mock.Mock
}

// 2. implement the interface
func (m *teamRepoMock) GetByID(id int) (repo.Team, error) {
    args := m.Called(id)

    return args.Get(0).(repo.Team), args.Error(1)
}

func (m *teamRepoMock) Update(team repo.Team) error {
    args := m.Called(team)

    return args.Error(0)
}

func TestUpdate(t *testing.T) {
    t.Run("GoodUpdate", func(t *testing.T) {
        // 3. instantiate/setup mock
        repoMock := new(teamRepoMock)
        repoMock.On("GetByID", 1).Return(repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"}, nil).Times(1)
        repoMock.On("Update", repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"}).Return(nil).Times(1)

        sut := NewTeamService(repoMock)
        err := sut.Update(&repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"})

        // 4. check that all expectations were met on the mock
        assert.Nil(t, err)
        assert.True(t, repoMock.AssertExpectations(t))
    })

    t.Run("BadUpdate", func(t *testing.T) {
        // 3. instantiate/setup mock
        repoMock := new(teamRepoMock)
        repoMock.On("GetByID", 1).Return(repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"}, nil).Times(1)
        repoMock.On("Update", repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"}).Return(errors.New("some error while updating")).Times(1)

        sut := NewTeamService(repoMock)
        err := sut.Update(&repo.Team{ID: 1, TeamOwnerID: 1, Name: "test"})

        // 4. check that all expectations were met on the mock
        assert.Equal(t, "some error while updating", err.Error())
        assert.True(t, repoMock.AssertExpectations(t))
    })
}

In the code you can find some comments to better detail what is happening. As you can guess, the problem is that this call is missing in the code:

repomock.on("getbyid", 1).return(repo.team{id: 1, teamownerid: 1, name: "test"}, nil).times(1)

If you run my solution it should work for you too.
If this solves your problem or you have any other questions, please let me know!

The above is the detailed content of Assertion: Mock: I don't know what to return because the method call was unexpected Error while writing unit test in Go. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete
Previous article:Parse URL in shell scriptNext article:Parse URL in shell script