Home > Software engineering >  assert: mock: I don't know what to return because the method call was unexpected Error while wr
assert: mock: I don't know what to return because the method call was unexpected Error while wr

Time:12-30

I am using testify in Go to write unit tests for my service methods and all the methods are working fine excepted the update method because in the update method I call another method("GetByID") of the same service inside the Update method.

Implementation of 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 method for update:

func (t *teamRepoMock) Update(team *team.Team) AppError {
    args := t.Called(team)
    if args.Error(0) != nil {
        return NewNotFoundError(args.Error(0))
    }

    return nil
}

Implementation of the test:

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 mock.On("GetByID") before and after I call .On("Update") in my test function implementation and it didn't work and also I modified the mockRepo Update function but it didn't work.

CodePudding user response:

Let me try to help you in figuring out the problem. I reproduced the repo with some simplification just to post the relevant code. If I'm not wrong in your solution there is a service (TeamService) that invokes some methods provided by an underlying package (TeamRepo). You wanna test the method Update method of the TeamService struct. After this recap, let me present the code first and I'll 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
}

Within this file, we can find the methods that we've to mock out. The methods are: GetByID and Update. Obviously, this is not your actual code but it doesn't matter for 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 the service that will be our System Under Test (sut) in our test code. Through Dependency Injection, we'll take advantage of the capabilities provided by the repo package that is injected via 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))
    })
}

Within the code, you can find some comments to better detail what's going on. As you've guessed the issue was this missing call in your 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 also for you.
Let me know if this solves your issue or if there are any other problems!

  • Related