如何在用例中循环测试Go Mock Repository?

huangapple go评论84阅读模式
英文:

How to test Go Mock Repository inside loop on usecase

问题

我有一个使用testify进行存储库模拟数据库测试的问题。

我想要测试一个服务/用例,它为每个迭代创建一个记录数据库。以下是代码:

这段代码包含数据库的模拟

mockrepository.go

package service

import(
  "errors"
  "github.com/stretchr/testify/mock"
)
type TestRepositoryMock struct {
	Mock mock.Mock
}

func (repository *TestRepositoryMock) CreateTodo(todo *Todo) error {
	arguments := repository.Mock.Called(todo)
	if arguments.Get(0) == nil {
		return errors.New("Error")
	} else {
		return nil
	}
}

func (repository *TestRepositoryMock) CreateTodoDetail(todo *TodoDetail) error {
	arguments := repository.Mock.Called(todo)
	if arguments.Get(0) == nil {
		return errors.New("Error")
	} else if arguments.Get(1) == nil {
		return errors.New("Error")
	}  else {
		return nil
	}
}

这个用例的逻辑是为每个项目数量(NumberOfItems变量)保存TodoDetail

usecase.go

package service

import(
  "strconv"
)

type UseCase interface {
  SaveTodo(numberOfItems int) (string, error)
}

func NewUseCase(repo Repository) UseCase {
  return &useCase{repo: repo}
}

type useCase struct {
  repo Repository
}

func (uc *useCase) SaveTodo(numberOfItems int) (string, error){
  todo := Todo{
    Title: "this is title",
  }
  uc.repo.CreateTodo(&todo)

  for i := 0; i < numberOfItems; i++ {
    todoDetail := TodoDetail{
      ID: todo.ID,
      Item: "item "+ strconv.Itoa(i),
    }
    uc.repo.CreateTodoDetail(&todoDetail)
  }

  return "success", nil 
}
package service

import(
  "testing"
  "github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

var repository = &TestRepositoryMock{Mock: mock.Mock{}}
var testService = useCase{repo: repository}

func TestService_SaveTodoSuccess(t *testing.T) {
  todo := Todo{
    Title: "this is title",
  }
  todoDetail := TodoDetail{
      ID: todo.ID,
      Item: "item 1",
    }
  repository.Mock.On("CreateTodo", &todo).Return(nil)
  repository.Mock.On("CreateTodoDetail", &todoDetail).Return(nil).Once()
  result, err := testService.SaveTodo(3)
  assert.Nil(t, err)
	assert.NotNil(t, result)
}

如何测试for循环中的多次调用CreateTodoDetail

英文:

I have a problem with testing with Repository Mock Database using testify.

I want to test a service/use case that creates a record database for each iteration. Here is the code:

This code contain mock of the database

mockrepository.go

package service

import(
  &quot;errors&quot;
  &quot;github.com/stretchr/testify/mock&quot;
)
type TestRepositoryMock struct {
	Mock mock.Mock
}

func (repository *TestRepositoryMock) CreateTodo(todo *Todo) error {
	arguments := repository.Mock.Called(todo)
	if arguments.Get(0) == nil {
		return errors.New(&quot;Error&quot;)
	} else {
		return nil
	}
}

func (repository *TestRepositoryMock) CreateTodoDetail(todo *TodoDetail) error {
	arguments := repository.Mock.Called(todo)
	if arguments.Get(0) == nil {
		return errors.New(&quot;Error&quot;)
	} else if arguments.Get(1) == nil {
		return errors.New(&quot;Error&quot;)
	}  else {
		return nil
	}
}

the logic inside this use case is to save TodoDetail for each number of items (NumberOfItems variable)

usecase.go

package service

import(
  &quot;strconv&quot;
)

type UseCase interface {
  SaveTodo(numberOfItems int) (string, error)
}

func NewUseCase(repo Repository) UseCase {
  return &amp;useCase{repo: repo}
}

type useCase struct {
  repo Repository
}

func (uc *useCase) SaveTodo(numberOfItems int) (string, error){
  todo := Todo{
    Title: &quot;this is title&quot;,
  }
  uc.repo.CreateTodo(&amp;todo)

  for i := 0; i &lt; numberOfItems; i++ {
    todoDetail := TodoDetail{
      ID: todo.ID,
      Item: &quot;item &quot;+ strconv.Itoa(i),
    }
    uc.repo.CreateTodoDetail(&amp;todoDetail)
  }

  return &quot;success&quot;, nil 
}
package service

import(
  &quot;testing&quot;
  &quot;github.com/stretchr/testify/assert&quot;
	&quot;github.com/stretchr/testify/mock&quot;
)

var repository = &amp;TestRepositoryMock{Mock: mock.Mock{}}
var testService = useCase{repo: repository}

func TestService_SaveTodoSuccess(t *testing.T) {
  todo := Todo{
    Title: &quot;this is title&quot;,
  }
  todoDetail := TodoDetail{
      ID: todo.ID,
      Item: &quot;item 1&quot;,
    }
  repository.Mock.On(&quot;CreateTodo&quot;, &amp;todo).Return(nil)
  repository.Mock.On(&quot;CreateTodoDetail&quot;, &amp;todoDetail).Return(nil).Once()
  result, err := testService.SaveTodo(3)
  assert.Nil(t, err)
	assert.NotNil(t, result)
}

How to test the multiple call for CreateTodoDetail inside for loop?

答案1

得分: 0

为了测试循环使用案例,我们应该提供一些模拟数据,与循环迭代次数相同。

例如,如果使用案例有一个循环迭代3次的循环,每次调用存储库三次,那么我们必须提供3个数据模拟。

代码应该像这样:

func TestService_SaveTodoSuccess(t *testing.T) {
  // 模拟实体 Todo
  todo := Todo{
    Title: "this is title",
  }

  // 模拟实体 TodoDetail
  todoDetail1 := TodoDetail{
      ID: todo.ID,
      Item: "item 0",
    }
  todoDetail2 := TodoDetail{
      ID: todo.ID,
      Item: "item 1",
    }
  todoDetail3 := TodoDetail{
      ID: todo.ID,
      Item: "item 2",
    }
  repository.Mock.On("CreateTodo", &todo).Return(todo)
  // 调用 3 次 "CreateTodoDetail" 存储库
  repository.Mock.On("CreateTodoDetail", &todoDetail1).Return(todoDetail1)
  repository.Mock.On("CreateTodoDetail", &todoDetail2).Return(todoDetail2)
  repository.Mock.On("CreateTodoDetail", &todoDetail3).Return(todoDetail3)
  result, err := testService.SaveTodo(3)
  assert.Nil(t, err)
  assert.NotNil(t, result)
}

这个场景构建了3个不同的 todoDetail 数据,以模拟3次迭代调用 CreateTodoDetail 存储库。

英文:

-SOLVED-

For testing on the loop use case, we should provide numbers of mock data. same as loop iteration.

Example. If use case has 3 iteration loop that calls the repository three times then we must provide 3 data mock.

the code should be like this:

func TestService_SaveTodoSuccess(t *testing.T) {
  // Mock Entity Todo
  todo := Todo{
    Title: &quot;this is title&quot;,
  }

  // Mock Entity TodoDetail
  todoDetail1 := TodoDetail{
      ID: todo.ID,
      Item: &quot;item 0&quot;,
    }
  todoDetail2 := TodoDetail{
      ID: todo.ID,
      Item: &quot;item 1&quot;,
    }
  todoDetail3 := TodoDetail{
      ID: todo.ID,
      Item: &quot;item 2&quot;,
    }
  repository.Mock.On(&quot;CreateTodo&quot;, &amp;todo).Return(todo)
// calls 3 times &quot;CreateTodoDetail&quot; Repository
  repository.Mock.On(&quot;CreateTodoDetail&quot;, &amp;todoDetail1).Return(todoDetail1)
  repository.Mock.On(&quot;CreateTodoDetail&quot;, &amp;todoDetail2).Return(todoDetail2)
  repository.Mock.On(&quot;CreateTodoDetail&quot;, &amp;todoDetail3).Return(todoDetail3)
  result, err := testService.SaveTodo(3)
  assert.Nil(t, err)
	assert.NotNil(t, result)
}

The scenario builds 3 different data on todoDetail to mock 3 times iterations for CreateTodoDetail repository

答案2

得分: 0

为了在一系列模拟响应中运行相同的测试,Mock提供了.Once()方法,可以重复使用单个模拟,并返回所需的每个结果。

// 循环遍历的数组
todoDetailArray := []TodoDetail{todoDetail1, todoDetail2}

// 针对每个测试场景循环遍历模拟
for eachTodoDetail := range todoDetailArray {
	repository.Mock.On("CreateTodoDetail", eachTodoDetail).Return(eachTodoDetail).Once()
	...
}
英文:

To run the same test over a range of mocked responses, Mock has a .Once() method that enables repeating a single mock with each result needed.

// array to loop through
todoDetailArray := []TodoDetail{todoDetail1, todoDetail2}

// loop through mocks for each test scenario
for eachTodoDetail := range todoDetailArray {
	repository.Mock.On(&quot;CreateTodoDetail&quot;, eachTodoDetail).Return(eachTodoDetail).Once()
	...
}

huangapple
  • 本文由 发表于 2022年4月11日 17:06:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/71825212.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定