为单个接口编写测试并为多个实现运行它们
Posted
技术标签:
【中文标题】为单个接口编写测试并为多个实现运行它们【英文标题】:Writing tests for a single interface and running them for multiple implementations 【发布时间】:2021-12-24 08:02:18 【问题描述】:假设我有如下界面:
type UserRepository interface
StoreUser(user User) (User, error)
以及该接口的以下实现:
type InMemoryRepository struct
...
func (repo InMemoryRepository) StoreUser(user User) (User, error)
...
type PostgresqlRepository struct
...
func (repo PostgresqlRepository) StoreUser(user User) (User, error)
...
我想为 StoreUser 方法编写一个单元测试。但是我只需要编写一次,因为该接口的所有实现的要求都是相同的。
为接口编写测试然后使用我拥有的任何实现运行它的最佳设计是什么?
到目前为止,我想出了一个:
a) 将测试包装在包含所有实现的测试用例循环中
testCases := []struct
name string
repo UserRepository
name: "Memory", repo: NewInMemoryRepository(),
name: "Postgresql", repo: NewPosrgresqlRepository(),
for _, tc := range testCases
t.Run(tc.name, func(t *testing)
...
b) 创建一个测试类
type UserRepositoryTests struct
UserRepository
func NewUserRepositoryTests(userRepository UserRepository) UserRepositoryTests
return UserRepositoryTests
UserRepository: userRepository,
func (userRepository UserRepositoryTests) TestStoreUser(t *testing.T)
...
并从实现包中调用它
type InMemoryUserRepository struct
func (ss InMemoryUserRepository) StoreUser(user users.User) (users.User, error)
func TestRunInterfaceTests(t *testing.T)
dsfnjkf := InMemoryUserRepository
testing := users.NewUserRepositoryTests(dsfnjkf)
testing.TestStoreUser(t)
虽然我觉得这两种方式都不对。
有更好的方法吗?
【问题讨论】:
选项a
对我来说似乎很好
@blackgreen 我不喜欢这种方法,因为我想把事情分开。我的设想是有人可以开发核心域逻辑(并为接口编写测试),然后其他人可以编写自定义实现而不更改核心域测试
【参考方案1】:
第一种方法是创建一个表并对其进行迭代,使用t.Run
运行每个项目,这在 Go 中是惯用的。这是我在这个场景中使用的方法,事实上我已经多次使用这种方法来实现完全相同的目的(同一个接口的多个实现)。
您描述的第二种方法在 Go 中很少见;将测试包装在多层中通常不习惯 - 测试应该简单明了。
关于您的评论;我认为您可能正在描述一种情况,即接口的编写者想要提供一个测试套件,其他人可以在他们的接口实现上调用该套件。听起来不错,但是为什么需要所有这些抽象级别?像 RunUserRepositoryTests
这样的函数就足够了 - 它需要你的实现和 *testing.T
。
【讨论】:
你说的完全正确,我在课堂上做得过火了。我最终创建了一个函数来收集所有测试,并从我的实现包中调用它。不过,我仍然想知道,第一种方法仍然是首选方法吗?以上是关于为单个接口编写测试并为多个实现运行它们的主要内容,如果未能解决你的问题,请参考以下文章
如何在单个 JUnit 测试中运行两个 Spring Boot 微服务?