How to mock individual `struct` function?

blov · · 528 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Assume, I have a struct that implements some interface methods:</p> <pre><code>type Worker interface { DoA(string) error DoB(string) error DoC(string) error } type MainWorker struct { } func (w *MainWorker) DoA(s string) error { // ... do something return nil } func (w *MainWorker) DoB(s string) error { // ... do something DoA(anything) // ... return nil } func (w *MainWorker) DoC(s string) error { // ... do something DoA(anything) // ... return nil } </code></pre> <p><code>DoC</code> and <code>DoB</code> functions call <code>DoA</code> function in some flows. </p> <p>Now, I would like to write unit tests. I write a test for every function in the interface, but I would like no to invoke real <code>DoA</code> when I&#39;m testing <code>Test_DoC</code> and <code>Test_DoB</code>, but I would like to stub this function only for these tests.</p> <blockquote> <p>How to do this?</p> </blockquote> <p>I&#39;ve tried to extract <code>DoA</code> into an additional interface, but have a feeling that this is a dead end. For the naive code above it will work but in practice, inter-function dependency call can be more complex and extracting every function called internally into an additional interface, looks like a big overhead to me.</p> <p>My current approach is as follows ...</p> <p>I&#39;ve defined a struct filed (of type <code>func</code>) for every function I want to mock. And then replace any single member, I want to test, with desired mock inside test. It works, but maybe there is a better approach. If you know one, please share.</p> <pre><code>type MainWorker struct { doA func(string) error doB func(string) error doC func(string) error } func NewWorker() Worker { w := new(MainWorker) w.doA = DoA w.doB = DoB w.doC = DoC } ... as before </code></pre> <p>and in test</p> <pre><code>// Mock Worker (with any mock lib) struct MockWorker { } func (m *MockWorker) DoA(s string) error { ... } ... DoB ... DoC ... // test func Test_DoB(t *testing.T) { mock := &amp;MockWorker{} worker := NewWorker() // replace single method &#39;DoA&#39; in this case, others keep intact worker.doA = mock.DoA() // call DoB and assert expectations worker.DoB() // it will call mock.DoA() inside ... } </code></pre> <hr/>**评论:**<br/><br/>hipone: <pre><blockquote> <p>DoC and DoB functions call DoA function in some flows.</p> </blockquote> <p>I think how and when DoC and DoB call DoA is an implementation detail and should not be tested nor enforced. If you have an interface with 3 methods and you want to ensure that implementer calls them in particular way or order internally - something smells utterly bad.</p> <p>Aside the comment above, you may want to implement a test utility - a spy mock object, that records and forwards calls to Worker interface.</p></pre>shovelpost: <pre><pre><code>type Worker interface { ... } func NewWorker() Worker { ... } </code></pre> <p>I believe you might be chasing the wrong problem here. You should try to have your <code>NewWorker</code> return a concrete implementation and not an interface. You can then test and pass that concrete type to any function that accepts a <code>Worker</code> interface which ideally should have as few methods as possible. Remember two things:</p> <ul> <li>Interfaces in Go are satisfied implicitly</li> <li>The larger the interface the weaker the abstraction</li> </ul> <p>Also the interface should ideally be defined <a href="https://github.com/golang/go/wiki/CodeReviewComments#interfaces" rel="nofollow">on the consumer and not the producer</a> aka not in the same package as the <code>NewWorker</code>. The interface should have only as many methods as the consumer needs (interface segregation principle).</p> <p>So in conclusion, test the concrete type and only mock the interface of the consumer when you test the consumer method/function.</p> <p>Have a look at: <a href="https://dave.cheney.net/2016/08/20/solid-go-design" rel="nofollow">SOLID Go Design</a></p></pre>alexei_led: <pre><p>Thank you, will take a look.</p></pre>alexei_led: <pre><p>What a great blog, you’ve recommended. Thank you.</p></pre>kapoof_euw: <pre><p>I&#39;m not entirely sure as to why you&#39;d want to mock out DoA(). Is it because it performs an action such as a DB call that is not strictly required to test DoB() and DoC()? If that is the case, mocking out the DB seems like the better option to me. Let DoA() execute but with predictable results.</p> <p>Generally though, I think more context regarding DoA() could help in finding a correct answer for your &#34;how to test/mock&#34; question :)</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

528 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传