Six line assert for testing without any library.

agolangf · · 609 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I am new to Golang, probably most of you guys here already figured this, obviously. Here is six line (eleven with imports) to check for function result using table driven test.</p> <pre><code>import ( &#34;reflect&#34; &#34;runtime/debug&#34; &#34;testing&#34; ) func assert(t *testing.T, exp, got interface{}, equal bool) { if reflect.DeepEqual(exp, got) != equal { debug.PrintStack() t.Fatalf(&#34;Expecting &#39;%v&#39; got &#39;%v&#39;\n&#34;, exp, got) } } </code></pre> <p>Then, I can use it like this in test function,</p> <pre><code>func Test... (t *testing.T) { input := []int{...} exp := []int{...} got := callFunctionToTest(input) assert(t, exp, got, true) } </code></pre> <hr/>**评论:**<br/><br/>weberc2: <pre><p>You&#39;ll also lose out on line numbers. Every failure will point to the <code>t.Fatalf()</code> line in your <code>assert()</code> function. I do something like this:</p> <pre><code>func assertEq(exp, got interface{}) error { if reflect.DeepEqual(exp, got) { return fmt.Errorf(&#34;Wanted %v; Got %v&#34;, exp, got) } } </code></pre> <p>And then in tests...</p> <pre><code>if err := assertEq(exp, got); err != nil { t.Fatal(err) } </code></pre> <p>It&#39;s a little more boilerplate, but it preserves line numbers. There may be a better way as well.</p></pre>gkx: <pre><p>Wait, shouldn&#39;t the first line of the function be</p> <pre><code>if !reflect.DeepEqual(exp, got) { </code></pre> <p>?</p> <p>Because right now I think it returns an error if they&#39;re the same.</p></pre>weberc2: <pre><p>Yes, that was a typo.</p></pre>karnd01: <pre><p>Calling the runtime.Caller function and telling it to skip is a way to get around this with less boilerplate see here for a simple example <a href="https://github.com/go-playground/assert/blob/v1/assert.go#L147" rel="nofollow">https://github.com/go-playground/assert/blob/v1/assert.go#L147</a></p></pre>weberc2: <pre><blockquote> <p><a href="https://github.com/go-playground/assert/blob/v1/assert.go#L147" rel="nofollow">https://github.com/go-playground/assert/blob/v1/assert.go#L147</a></p> </blockquote> <p>I&#39;ve looked at things like this before, but it seems more &#34;conventional&#34; in some sense to make the caller handle an error instead of passing in a &#39;skip&#39; value, even if the error handling is more keystrokes.</p></pre>karnd01: <pre><p>I suppose but a library handling the error greatly reduces repetitive code, besides all you really need in tests is simple assertion of line number and what went wrong, handling your own errors seems like allot of extra work.</p> <p>P.S. In my assertion library the Caller doesn&#39;t pass the skip value, the IsEqulSkip function my link referenced was just to show how getting the real line number can be accomplished, you can see in the README <a href="https://github.com/go-playground/assert/blob/v1/README.md" rel="nofollow">https://github.com/go-playground/assert/blob/v1/README.md</a> all the user has to do is call Equal(t, val1,val2) IsEqualSkip is just exposed to allow your own assertion functions to use the back end of the assertion library like I do here <a href="https://github.com/go-playground/validator/blob/v8/validator_test.go#L116" rel="nofollow">https://github.com/go-playground/validator/blob/v8/validator_test.go#L116</a></p></pre>rv77ax: <pre><blockquote> <p>You&#39;ll also lose out on line numbers. </p> </blockquote> <p>Thats why I add <code>debug.PrintStack()</code> to get the last call of assert.</p></pre>weberc2: <pre><p>Oh, cool. I missed that. I&#39;ll have to try that sometime.</p></pre>GopherFromHell: <pre><blockquote> <p>more boilerplate, but it preserves line numbers. There may be a better way as well</p> </blockquote> <p>you can have control over that (depth in the call stack). the Output method of a log.Logger allows you to set the call depth. look <a href="https://github.com/heliorosa/gamehouse/blob/master/log/log.go#L21" rel="nofollow">here</a> </p></pre>shazow: <pre><p>I usually start out with something like this:</p> <pre><code>if got, want := somefunc(), &#34;expected value&#34;; !reflect.DeepEqual(got, want) { t.Errorf(&#34;got %q; want %q&#34;, got, want) } // ... mutate state if got, want := anotherfunc(), &#34;another value&#34;; !reflect.DeepEqual(got, want) { t.Errorf(&#34;got %q; want %q&#34;, got, want) } </code></pre> <p>Then sometimes it expands into more complicated assignments and checks, so I pull them out into separate assignments and maybe reuse the got/want variables. </p> <pre><code>got = ... expect = ... if !reflect.DeepEqual(got, want) { t.Errorf(&#34;got %q; want %q&#34;, got, want) } </code></pre> <p>Then if I&#39;m doing exceptionally weird checking, maybe it requires reading in from a channel and sync&#39;ing outputs, <em>then</em> I&#39;ll make a separate specialized assert helper.</p></pre>karnd01: <pre><p>Just a word of caution reflect.DeepEqual will work in the majority of cases, but has a little more difficulty when you start getting into some more complex scenarios such as passing an interface of a pointer, into the assert function, I wish I could dig up a good example but on my cell right now.</p> <p>I use reflect.DeepEqual as well in a very small assertion library and had to do it like this to handle all the strange scenarios <a href="https://github.com/go-playground/assert/blob/v1/assert.go#L13" rel="nofollow">https://github.com/go-playground/assert/blob/v1/assert.go#L13</a></p> <p>It&#39;s not pretty but gets the job done.</p></pre>rv77ax: <pre><blockquote> <p>interface of a pointer</p> </blockquote> <p>Why would one will do that? I really like to see real world example for that. I can wait :)</p></pre>karnd01: <pre><p>I&#39;ll try and dig one up tonight, I know it is in these tests here that caused the issue <a href="https://github.com/go-playground/validator/blob/v8/validator_test.go" rel="nofollow">https://github.com/go-playground/validator/blob/v8/validator_test.go</a></p> <p>But as you can see there are hundreds of assertions.</p></pre>karnd01: <pre><p>ok so found a few simple examples that IMHO reflect.DeepEqual does not report properly regarding pointers passed as interface (or however you want to describe it)</p> <p><a href="https://play.golang.org/p/jhKDWbYTVs" rel="nofollow">https://play.golang.org/p/jhKDWbYTVs</a></p> <p>myMap, i and s should report back as true, but don&#39;t and that&#39;s why I use my small assertion library <a href="https://github.com/go-playground/assert" rel="nofollow">https://github.com/go-playground/assert</a></p></pre>jsabey: <pre><p>You could try out my unittest package, it&#39;s a fairly small package. It also has a stacktrace on failure so you&#39;ll get the line number of the failed test if the code is available locally: <a href="https://github.com/sabey/unittest" rel="nofollow">https://github.com/sabey/unittest</a></p> <p>It only supports Equals, EqualsExact, IsNil, NotNil</p> <p>It doesn&#39;t support comparing slices but you could just do: &#34;Equals(t, reflect.DeepEqual([]int{1, 2}, []int{1, 2}), true)&#34;</p></pre>ThisAsYou: <pre><p>I like the way the <a href="https://github.com/stretchr/testify/blob/master/assert/assertions.go" rel="nofollow">testify/assert</a> package does it. <code>assert.Equal(..)</code> and <code>assert.NotEqual(...)</code> are much more clear than having a single function with a boolean. So if I really had to avoid the external library, I&#39;d probably just re-implement at least those 2 functions for clarity.</p></pre>

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

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