英文:
How to mock standard package function for unit test
问题
我有三个函数:
func IsSymlinks(path string) {
...
...
}
func (c *MyClass) myFunc1(path string) {
...更多代码
...更多代码
if IsSymlinks(path) {
realPath := filepath.EvalSymlinks(path)
}
...更多代码
...更多代码
}
func myFunc2(path string) {
...更多代码
...更多代码
if IsSymlinks(path) {
realPath := filepath.EvalSymlinks(path)
}
...更多代码
...更多代码
}
我如何通过模拟filepath.EvalSymlinks
和IsSymlinks
来测试myFunc1
和myFunc2
函数?我搜索了一些帖子并看到了几种解决方案。
-
创建一个函数类型的
EvalSymlinks
变量,用filepath.EvalSymlinks
进行初始化,然后在测试包中将其更改为我的实现。然而,我更倾向于不使用这种方法。 -
将
filepath.EvalSymlinks
和IsSymlinks
作为参数传递给myFunc
。我也不太喜欢使用这种方式。 -
很多人谈论使用接口进行模拟。你能帮忙吗?或者还有其他可以测试
myFunc
的方法吗?谢谢! -
我还在考虑的另一个选择是,在进行函数测试之前,是否将一个符号链接创建到一个路径中,然后在测试完成后立即删除该符号链接?
英文:
I have three functions:
func IsSymlinks(path string) {
...
...
}
func (c *MyClass) myFunc1(path string) {
...more code
...more code
if IsSymlinks(path) {
realPath := filepath.EvalSymlinks(path)
}
...more code
...more code
}
func myFunc2(path string) {
...more code
...more code
if IsSymlinks(path) {
realPath := filepath.EvalSymlinks(path)
}
...more code
...more code
}
How do I test myFunc1 and myFunc2 by mocking filepath.EvalSymlinks and IsSymlinks? I searched some posts and saw several solutions.
-
Create a EvalSymlinks variable of function type, initialized with filepath.EvalSymlinks, and then in the test package, change it to my implementation. However, I prefer not to use this approach.
-
Pass in filepath.EvalSymlinks and IsSymlinks into myFunc as parameter. I prefer not to use this way either.
-
A lot of people talk about mocking use interface. Could you please help? Or another approach that could test myFunc? Thank you!
-
Another option I am thinking is if it is a good practice to create a symlink in os to a path before I do the function test, and then delete the symlink right after testing?
答案1
得分: 3
你可以通过使用接口来实现。例如,假设你有一个名为Linker的接口:
type Linker interface {
IsSymlinks(path string)
}
现在你可以将一个Linker对象嵌入到调用IsSymlinks
方法的函数中:
type MyClass struct {
Linker
}
func (p *MyClass) myFunc(path string) {
_ = p.Linker.IsSymlinks(path)
}
现在在你的测试中,你可以创建一个模拟的Linker:
type mockLinker struct{}
// 根据需要实现mockLinker上的IsSymlinks方法
p := &MyClass{
Linker: mockLinker{},
}
p.myFunc(path)
当p.myFunc
调用IsSymlinks
方法时,它会调用你模拟的IsSymlinks
方法。
对于filepath.EvalSymlinks(path)
,你可以将该方法包装在另一个方法中,并将新方法添加到相同的接口中,然后模拟该方法而不是模拟EvalSymlinks
。
仅为了测试目的而创建接口可能并不总是最好的主意,因为在某些情况下,这可能导致拥有大型接口,这在Go中不符合惯例,因为它会使代码变得不太可读。
英文:
You can do it by using an interface. For example let's say you have an interface called Linker:
type Linker interface {
IsSymlinks(path String)
}
Now you can embed a Linker object into the function that calls the IsSymlinks
method.
type MyClass struct {
Linker
}
func (p *MyClass) myFunc(path String) {
_ = p.Linker.IsSymlinks(path)
}
Now in your test you can create a mock Linker.
type mockLinker struct{}
//implement the IsSymlinks on the mockLinker as you wish
p := &MyClass{
Linker: mockLinker,
}
p.myFunc(path)
when p.myFunc
reaches the IsSymlinks
method it calls your mocked IsSymlinks
method.
For filepath.EvalSymlinks(path)
you can wrap that method in another method that is yours and add the new method to the same interface and mock that method instead of mocking the EvalSymlinks
.
Creating interfaces just for the purpose of testing may not always be the best idea as in some cases may result in having large interfaces which are not idiomatic in Go as it makes the code less readable.
答案2
得分: 1
我认为你可以尝试以不同的方式设计你的MyClass结构和相关函数。我的意思是让你的MyClass对象与filepath等包保持独立。首先,创建一个新的接口,其中包含两个函数:
type MyEvalLink interface {
IsSymlinks(path string) bool
EvalSymlinks(path string) (string, error)
}
然后,你可以在一个使用filepath包或虚拟代码的结构体中实现这两个函数:
type EvalLinkUse struct{}
func (p *EvalLinkUse) IsSymlinks(path string) bool {
// 在这里放入你的真实代码或虚拟代码
}
func (p *EvalLinkUse) EvalSymlinks(path string) (string, error) {
// 在这里放入你的真实代码或虚拟代码
}
这样,你的代码将会改变如下:
type MyClass struct {
...你的代码
MyEvalLink MyEvalLink
}
func (c *MyClass) myFunc1(path string) {
...更多的代码
...更多的代码
if c.MyEvalLink.IsSymlinks(path) {
realPath, err := c.MyEvalLink.EvalSymlinks(path)
// 在这里处理realPath和err
}
...更多的代码
...更多的代码
}
这样做是否适合你的情况?
英文:
I think you can try to design in a different way your MyClass struct and the related functions. I mean to get indipendent your MyClass object from packages like filepath. First, create a new interface that have two functions:
type MyEvalLink interface {
IsSymlinks(path string) bool
EvalSymlinks(path string) (string, error)
}
Then you can implement both the two functions in a struct that use the filepath package or a fake code:
type EvalLinkUse struct{}
func (p *EvalLinkUse) IsSymlinks(path string) bool {
// put here your real code or fake
}
func (p *EvalLinkUse) EvalSymlinks(path string) (string, error) {
// put here your real code or fake
}
So, your code will change as follow:
type MyClass struct {
...your code
MyEvalLink MyEvalLink
}
func (c *MyClass) myFunc1(path string) {
...more code
...more code
if c.MyEvalLink.IsSymlinks(path) {
realPath := c.MyEvalLink.EvalSymlinks(path)
}
...more code
...more code
}
Could that fit your case?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论