<p>hello to all, i want to write some unit tests for this package:</p>
<pre><code>package dirService
func () Create(path string) bool {
if !d.Exist(path) {
err := os.Mkdir(path, 0777)
if err!=nil{
return false
}
return true
}
return false
}
</code></pre>
<p>But i dont want to create unnecessary directories while testing. </p>
<p>(Yeah i know its not a good method to test but consider this a db inserter or equivalent. Dont want to write real db.)</p>
<p>I did write some unit test in c# and java before, so i know that i have to use mock objects to achieve this, I checked on the internet for golang:</p>
<ul>
<li><a href="https://husobee.github.io/golang/testing/unit-test/2015/06/08/golang-unit-testing.html" rel="nofollow">https://husobee.github.io/golang/testing/unit-test/2015/06/08/golang-unit-testing.html</a> </li>
<li><a href="https://jonathanmh.com/golang-unit-testing-for-absolute-beginners/" rel="nofollow">https://jonathanmh.com/golang-unit-testing-for-absolute-beginners/</a></li>
</ul>
<p>Implementation:</p>
<p>First the interface:</p>
<pre><code>type DirCreator interface {
Create(path string) bool
}
</code></pre>
<p>And a concrete struct like this:</p>
<pre><code>type DirService struct{}
func (d *DirService) Create(path string) bool {
err := os.Mkdir(path, 0777)
if err!=nil{
return false
}
return true
}
</code></pre>
<p>And now i have to write a method that accepts this interface as a parameter in order to inject dependencies:</p>
<pre><code>func Create(d DirCreator, path string) bool {
return d.Create(path)
}
</code></pre>
<p>Now i can create a "mock" struct that implements the interface :</p>
<pre><code>type MockDirService struct {
MockCreate func(path string) bool
}
func (m *MockDirService) Create(path string) bool {
return m.MockCreate(path)
}
</code></pre>
<p>And finally a unit test:</p>
<pre><code>func TestCreate(t *testing.T) {
var _ = Describe("Create", func() {
var mockDirService = &MockDirService{
MockCreate: func(path string) bool {
//code to throws error
return false
},
}
It("Should Return false when error occurs", func() {
Expect(false, Create(mockDirService, "/path/that/exists"))
})
})
}
</code></pre>
<p>For this one method (Create) i implement a lot of codes to test, i'm terrifying about implementing 3 or 4 methods. This is not fun, imo mocking is not easy in golang, is there another way to achieve this problem? i want to discuss this with experienced people out here.</p>
<hr/>**评论:**<br/><br/>saturn_vk: <pre><p>I've used goautomock to generate mock implementations for testing, and so far I'm quite happy with it:
<a href="https://github.com/ernesto-jimenez/goautomock" rel="nofollow">https://github.com/ernesto-jimenez/goautomock</a></p></pre>TheMerovius: <pre><p>My usual advice is "Write fakes, not mocks". If nothing else, it makes writing Mocks easier. So, instead of creating a MockDirService, you write, e.g.</p>
<pre><code>type FakeDirService struct {
dirs map[string]bool
}
func NewFakeDirService() *FakeDirService {
return &FakeDirService{
dirs: make(map[string]bool),
}
}
func (d *FakeDirService) Create(path string) bool {
exists := d.dirs[path]
d.dirs[path] = true
return !exists
}
</code></pre>
<p>You can then use this fake in any tests you have to get isolated behavior. If you want to inject errors, you can selectively mock out methods:</p>
<pre><code>type MockDirService struct {
CreateFunc func(path string) bool
DirService
}
func (m *MockDirService) Create(path string) bool {
if m.CreateFunc != nil {
return m.CreateFunc(path)
}
return m.DirService.Create(path)
}
func TestFoo(t *testing.T) {
f := NewFakeDirService()
m := &MockDirService{
DirService: f,
CreateFunc: func(path string) bool {
if path == "/inject/an/error" {
return false
}
return f.Create(path)
},
}
DoAThing(m)
}
</code></pre>
<p>Now, all of that being said: In Go, there usually are no "canonically correct patterns". In general, you should try to use the actual dependencies as much as possible and use fake implementations as far down the stack as you can. That way, you prevent a lot of bad test-outcomes, like testing implementation, instead of testing behavior, or testing your <em>Fake</em>, instead of testing your production code. Especially, if the thing you are faking has significant business-logic that you'd need to duplicate - in that case, it would make more sense, to use the real deal, but maybe fake out <em>its</em> dependencies.</p>
<p>For example, if you are using a Database, it probably makes more sense, to start the actual database on a temporary directory and pass a connection to it to your production code, instead of creating a fake (or mock). In particular, I would only use mocks if you want to test the error-handling (and don't just inject errors to enumerate to your error-handling paths, but check actual pathological error conditions, like interruptions or corrupt data).</p>
<p>Writing good tests is hard. And there isn't "the right" way to do it, sadly.</p>
<p>[edit] oh and to illustrate further: In your case, you probably should neither fake, nor mock, but just create a temporary directory and point your production code at that for all its fs-operations.</p></pre>drvd: <pre><p>Please No! Stop it! This hurts!</p>
<p>You have a function which's purpose is to create a directory or fail in certain conditions. If you want to test that you will <em>have</em> to create a directory. Stop that mock nonsense and that "unit test must not access whatever" crap. If the sole purpose of the unit under test is writing to the filesystem you have to write to the filesystem.</p>
<p>The go tool handles the directory "testdir" especially: You can add test files and directories to it and it is not considered a package. Very handly. Set up a file structure which contains all the cases you are interested in testing, like "directory does not exist but parent is write protected" or "directory already exists". Then write test. The only thing to remember is to clean up created stuff. That's simple, defer that code.</p>
<p>Take a look at how the standard library of Go does it's testing. There is no ultrasharp and clear border between "unit" and "integration" testing. Just stay sensible.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传