<div dir="ltr">I'm glad to hear Roger's opinion about testing internal code... that's exactly how I feel.  True unit tests of small bits of code are easy to write, easy to read and understand, and give you confidence that your code is doing what you think it'll do in a wide variety of cases.  If the unit test fails, it's generally incredibly clear where the fault in the code lies, and it's easy to fix either the code or the test.  In 6 months, you or someone else can go back to the tests, easily understand what they are testing, and modify them trivially when making a bug fix or improvement.  <div><br></div><div>While you certainly *can* write code that tests all the corner cases of some small utility function through the external API... those tests will almost always be incredibly opaque and hard to understand, and generally much more complicated than they would be if you could just test the utility function by itself.  This is often a problem I have with our current tests... it's hard to see what is actually being tested, and even harder to verify that the test itself is correct and complete.  When a test fails, you often have no clue where the actual problem lies, because the test traverses so much code.  <div><br></div><div>Of course, I definitely think you *also* need tests of the exported API of a package... but small unit tests are still incredibly valuable.</div><div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jan 22, 2016 at 7:06 AM roger peppe <<a href="mailto:roger.peppe@canonical.com">roger.peppe@canonical.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 22 January 2016 at 09:40, William Reade <<a href="mailto:william.reade@canonical.com" target="_blank">william.reade@canonical.com</a>> wrote:<br>
> I do not want anyone to add unit tests for non-exported code, because those<br>
> tests are almost completely worthless.<br>
<br>
I'd beg to disagree with this. I think it very much depends what you are<br>
testing. For me tests are about giving confidence that the code works.<br>
Sometimes as part of the implementation of a package I'll write a<br>
function with a well defined contract that doesn't justify being made<br>
public because it's intimately related to the way that the package is<br>
implemented. The function might be reasonably tricky, and it may be hard<br>
to reach all its corner cases at arm's length through the public API.<br>
It might not even be possible, but that doesn't mean that it's not<br>
useful to test the function, as it may provide a foundation for more<br>
varied future functionality.<br>
<br>
In this kind of scenario, I think it makes good sense to write a unit test<br>
for the unexported function. If you change the implementation, you might<br>
throw away the function and its tests, and that's fine, but this approach,<br>
in my experience, can give good confidence in some tricky situations with<br>
significantly less effort than doing everything through the public API.<br>
<br>
As always, there's a trade-off here - we want to maximise confidence<br>
while minimising time and effort spent writing and maintaining the<br>
code. It's always going to be a judgement call and flat assertions like<br>
"those tests are almost completely worthless" are not that helpful IMHO.<br>
<br>
I do agree that writing external tests *when reasonable* is the way,<br>
and I think that export_test.go is a reasonable escape hatch for the<br>
times when it's useful to write internal tests.<br>
<br>
Nate, you seem to be arguing that because we can't have total separation<br>
between external and internal APIs, then we should not have any separation<br>
at all. I don't agree - I think that keeping as much as possible to<br>
the external API, but having a few well-defined exported objects (in<br>
export_test.go) works well, and reflects the nuanced situation that we<br>
actually live in.<br>
</blockquote></div>