Rhi*_*rva 6 testing unit-testing go
假设我有一个UserRepository结构,它包含了与数据库交互的逻辑.这个结构有一组方法,如:
还有另一个结构(让我们称之为UserService),它依赖于UserRepository结构.
要测试UserService,我需要模拟UserRepository的功能.我知道这样做的唯一方法是为UserRepository提供接口,并使UserService依赖于它而不是UserRepository结构.它将允许创建一个模拟的接口实现,并将其设置为测试中UserService的依赖项.
最惯用的方法是什么?
1)如果UserService仅依赖于1个UserRepository的方法(比如说findAll) - 我是否还应该定义一个具有所有存储库方法的接口,或者最好只为此方法定义一个单独的接口并将其用作UserService的依赖项?如果是这样,它的最佳名称(界面)是什么?如果另一个结构将依赖于findAll()和findById()方法,我应该再次创建另一个接口吗?
2)哪里是存储这些接口的模拟的最佳位置?是否可以重复使用它们?或者对于不同结构的测试,我需要重新定义模拟?
PS对我来说单元测试是项目中非常重要的一部分.我想让它们尽可能可读,避免使用样板代码并专注于它们的逻辑.因此,在不同的测试文件中为相同的接口创建几个模拟实现会给我一个不好的选择,因为它使测试代码的可读性降低.
1)我会同意 elevine 所说的,即只需要该结构所需的方法。例如:您有需求UserService和FindByName,FindAll并且UserAdminService需要FindById和FindAll。Save在这种情况下,您应该有两个接口:
UserProvider与FindByName和FindAllUserAdminProvider与FindById,FindAll和Save.这也可以让您保持UserProvider控制,例如您知道它无法调用Save,因此它无法修改用户。
您可能只需要一种满足这两个接口的实际实现。
2)查看testify/mock和mockery。Mockery 会在子包中为您的接口生成模拟mocks,每个接口一个。这确实意味着您不能对两个测试使用相同的模拟结构,但这并不重要,因为代码是生成的。您不会在模拟结构中模拟接口的行为,而是通过设置期望在测试中进行操作,例如:
func TestThatYouCantLoginWithNonexistentUser(t *testing.T) {
userRepository := new(mocks.UserRepository)
userService := user.NewService(userRepository)
// if the userService calls UserRepository.FindByName("joe"), it will return nil, since there's no such user.
userRepository.On("FindByName", "joe").Return(nil)
_, err := userService.Login("joe", "password")
// err should not be nil and the message should be "user does not exist"
assert.EqualError(t, err, "user does not exist")
// assert that the expectations were met, i.e. FindByName was called with "joe"
userRepository.AssertExpectations(t)
}
Run Code Online (Sandbox Code Playgroud)
这实际上使测试很容易理解,因为您不必去检查其他文件,当您调用FindByName("joe").