英文:
How to override mocked calls expectations in table driven tests
问题
在进行表驱动测试时,我使用了由mockery
生成的一些模拟对象,并设置了一些方法调用的期望,这些期望依赖于为每个测试用例提供的数据集。我遇到的问题是,模拟调用始终返回第一个测试用例中期望的结果集,而不是当前正在执行的测试用例所定义的结果集。
当我运行这个测试时,第二个测试用例失败,因为结果是1
,而不是预期的2
,我可以看到问题是模拟方法返回了1
(第一个测试用例设置的值),而不是2
(当前测试用例设置的值)。
有什么办法可以解决这个问题吗?
英文:
In working on a table-driven test where I'm using some mocks generated by mockery
and set some method-call expectations that depend on the data provided in the data set for each test case. I'm facing the issue that the mocked call is returning always the result set for the expectation in the first test case instead of the one defined for the test case bein executed.
func (s *MyTestSuite) TestMySampleTest() {
testCases := []struct {
Name string
Requests []*service.CreateCredentialRequest
}{
{
Name: "first case",
mockedResult: 1,
expected: 1,
},
{
Name: "second case",
mockedResult: 2,
expected: 2,
},
}
for _, tc := range testCases {
s.Run(tc.Name, func() {
s.someMock.On("SomeMethodCall", mock.Anything).Return(tc.mockedResult)
result := s.SUT.SomeMethodThatCallsTheMockedObe()
s.Equal(expected, result)
})
}
}
When I run this test, it fails for the second case because the result is 1
instead of 2
as expected, and I can see that the problem is the mocked method is returning 1
(the value set for the first test case) instead of 2
(the value set for the current test case).
Any idea on how to fix it?
答案1
得分: 1
这可能不是最优雅的解决方案,我想知道是否有其他方法可以做到,但目前为止,我找到了这个解决方案,可以解决问题。它的思路是为表驱动测试中的每个子测试生成一个新的模拟实例,因此在每个子测试中,我们使用一个全新的模拟实例,它没有从前一个子测试中设置的任何期望。考虑到我正在使用testify.Suite
来组织和处理我的测试,这样做就像在每个子测试中手动调用s.SetupTest()
方法一样简单:
// SetupTest在每次运行测试之前执行,我在这里实例化SUT及其依赖项。
func (s *MyTestSuite) SetupTest() {
// 实例化模拟对象
s.someMock = mocks.NewSomeMock(s.T())
// 实例化SUT,并通过构造函数将模拟对象注入
s.SUT = NewSUT(s.someMock)
}
func (s *MyTestSuite) TestMySampleTest() {
testCases := []struct {
Name string
Requests []*service.CreateCredentialRequest
}{
// 测试用例在这里
}
for _, tc := range testCases {
s.Run(tc.Name, func() {
// 在每个子测试中手动调用s.SetupTest()以生成模拟对象的新实例。
// 如果不这样做,模拟对象将始终返回第一个设置的期望(即第一个测试用例设置的期望)。
s.SetupTest()
// 这里是之前的逻辑
s.someMock.On("SomeMethodCall", mock.Anything).Return(tc.mockedResult)
result := s.SUT.SomeMethodThatCallsTheMockedObe()
s.Equal(expected, result)
})
}
}
希望这可以帮助到你!
英文:
It isn't probably the most elegant solution and I would like to know if there is any other way of doing it, but for the time being, I've found this solution that makes the trick. It consists of generating a new mock for every subtest run by the table-driven test, so in every subtest we use a fresh new mock instance that doesn't have any expectations set from a previous subtest. Taking into account that I'm using testify.Suite
to organize and handle my tests, doing this is as easy as manually calling the s.SetupTest()
method in every subtest:
// SetupTest is executed before every test is run, I instantiate the SUT and
// its dependencies here.
func (s *MyTestSuite) SetupTest() {
// Instantiate the mock
s.someMock = mocks.NewSomeMock(s.T())
// Instantiate the SUT, injecting the mock through the constructor function
s.SUT = NewSUT(s.someMock)
}
func (s *MyTestSuite) TestMySampleTest() {
testCases := []struct {
Name string
Requests []*service.CreateCredentialRequest
}{
// test cases here
}
for _, tc := range testCases {
s.Run(tc.Name, func() {
// Manually calling s.SetupTest() to generate new instances of the mocks in every subtest.
// If we don't do this, the mock will always return the first expectation set (the one set for the first test case).
s.SetupTest()
// Here comes the logic of the text as we had it before
s.someMock.On("SomeMethodCall", mock.Anything).Return(tc.mockedResult)
result := s.SUT.SomeMethodThatCallsTheMockedObe()
s.Equal(expected, result)
})
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论