假设我有一个UserRepository结构,它包含了与数据库交互的逻辑.这个结构有一组方法,如:
还有另一个结构(让我们称之为UserService),它依赖于UserRepository结构.
要测试UserService,我需要模拟UserRepository的功能.我知道这样做的唯一方法是为UserRepository提供接口,并使UserService依赖于它而不是UserRepository结构.它将允许创建一个模拟的接口实现,并将其设置为测试中UserService的依赖项.
最惯用的方法是什么?
1)如果UserService仅依赖于1个UserRepository的方法(比如说findAll) - 我是否还应该定义一个具有所有存储库方法的接口,或者最好只为此方法定义一个单独的接口并将其用作UserService的依赖项?如果是这样,它的最佳名称(界面)是什么?如果另一个结构将依赖于findAll()和findById()方法,我应该再次创建另一个接口吗?
2)哪里是存储这些接口的模拟的最佳位置?是否可以重复使用它们?或者对于不同结构的测试,我需要重新定义模拟?
PS对我来说单元测试是项目中非常重要的一部分.我想让它们尽可能可读,避免使用样板代码并专注于它们的逻辑.因此,在不同的测试文件中为相同的接口创建几个模拟实现会给我一个不好的选择,因为它使测试代码的可读性降低.
简单的例子。
我有包裹xxx。该软件包包含:
结构C,它是B方法中的一个参数
type A struct {
SomeField B
}
type B interface {
SomeMethod(c C)
}
Run Code Online (Sandbox Code Playgroud)现在想象一下,我想为结构A和模拟依赖项B创建单元测试。为创建模拟,我正在使用模拟生成器。所有模拟都存储在公用的“ mocks”文件夹中。
问题在于生成的模拟对xxx包具有依赖性。这是因为接口B的SomeMethod具有参数xxx.C。
每当我尝试将我的模拟结构导入a_test.go时,都会由于循环导入问题而失败。xxx程序包在a_test.go中导入了模拟程序包。并且模拟程序包在我生成的模拟程序中导入xxx程序包。
我需要一个平和的建议,对此最好的解决方法是什么?也许我的方法不够习惯。您将模拟物存储在哪里?
你能解释一下组织单元测试的正确方法吗?例如,如果想模拟我的struct依赖项,我需要创建一个"实现"某个接口的模拟依赖项.
我应该在哪里创建这个模拟?它应该在同一个测试文件中创建吗?但是如果我在另一个测试中需要它呢?Go不允许在同一个包的2个不同文件中定义具有相同名称的结构(例如UserServiceMock).那么定义这个模拟结构的最佳位置是什么?
另一个问题.我应该自己实现这种模拟还是有一些库/工具可以让我这样做?
让我们想象一下我有一张地图:map[string]string
.我想获取此地图的排序键列表.所以我可以这样做:
func SortedMapKeys(m map[string]string) (keyList []string) {
for key := range m {
keyList = append(keyList, key)
}
sort.Strings(keyList)
return
}
Run Code Online (Sandbox Code Playgroud)
然后我会有另一个类型的地图map[string]bool
.我也想得到它的钥匙.但问题是函数SortedMapKeys接受一个map[string]string
参数.所以我需要编写完全相同的函数,只有一个区别 - 它会接受一个map[string]bool
.
出于显而易见的原因,它不是一种选择.如果有一天我想改变我获取和排序键的逻辑,我将需要跟踪和更新所有这些功能.此外,我将不得不为所有这些函数编写相同的单元测试,这些函数实际上做同样的事情,因为它们的主体是100%相等(代码重复).
有没有办法创建一个可以接受map[string]
任何东西的通用函数?
下面是一个例子:
func main() {
c := make(chan int)
i := 0
go goroutine(c)
c <- i
time.Sleep(10 * time.Second)
}
func goroutine(c chan int) {
for {
num := <- c
fmt.Println(num)
num++
time.Sleep(1 * time.Second)
c <- num
}
}
Run Code Online (Sandbox Code Playgroud)
我在 goroutine 中尝试做的是从通道接收数字,打印它,递增并在一秒钟后将其发送回通道。在此之后,我想重复这个动作。
但因此,操作只进行一次。
输出:
0
Run Code Online (Sandbox Code Playgroud)
难道我做错了什么?
go ×5
testing ×2
unit-testing ×2
channel ×1
dictionary ×1
goroutine ×1
list ×1
mocking ×1
sorting ×1