The case of somelib.Bar() can be solved by replacing it with an interface. 
You can than inject a Bar function that always returns an error.

Otherwise I would recommend to use fuzzers to increase code coverage. They 
will do a much better job than you do. For byte sequence interfaces that 
don't involve checksums or are encrypted they are extremely good in finding 
edge cases. I have no experience for business logic, but I would give a 
JSON encoding of the input parameters a shot.

Kind regards,

Ulrich


On Thursday, February 4, 2021 at 10:35:42 PM UTC+1 chat...@google.com wrote:

> Hey all,
>
> Thomas, I agree that the code you provide would benefit from unit tests, 
> but I would like to focus my question on the very common case which simply 
> throws the error without additional edge cases, such as the example given 
> in the first email.
>
> Looking at the feedback given so far, I think we have:
>
> - So far, we don't have quantitative literature on how this pattern 
> impacts code coverage in Go versus other languages (Axel)
> - Reconsider using code coverage as the primary metric for code quality 
> (Axel)
>   - Understood, but it is still a common metric and we should be ready to 
> answer questions about how it relates to Go.
> - Does it make sense to increase code complexity to do this additional 
> unit testing (Szczepan)
>   - Yes, I think that is the essence of my problem; for what it's worth, 
> this <https://play.golang.org/p/Ieb8AnbeTUp> seems to be an easy way to 
> mock it, but feels non-idiomatic
>   - Also, apologies, but I can't share actual source code or point to 
> examples, but they should be very common
>
> Thanks all for the feedback!
>   Charles
>
> On Thu, Jan 28, 2021 at 12:42 PM 'Thomas Bushnell BSG' via golang-nuts <
> golan...@googlegroups.com> wrote:
>
>> IMO:
>>
>> To give these things names, you have:
>>
>> func Foo(...) ..., error {
>>   do things here...
>>   err := makeASubroutineCall(...)
>>   if err != nil {
>>     return ..., err
>>   }
>>   do more things
>> }
>>
>> And we suppose that makeASubroutineCall is already will tested and you 
>> know it returns errors correctly. What is the point of a separate test of 
>> Foo that makes sure that it returns the errors in the same cases?
>>
>> Suppose Foo had a bug, and *stopped* returning that error. Wouldn't you 
>> want to know? Especially since in many cases, not returning that error can 
>> cause the rest to subtly seem right (perhaps makeASubroutineCall syncs 
>> something to disk or kicks off some secondary process....)...
>>
>> Someone comes along, see, and your function becomes this:
>>
>> func Foo(...) ..., error {
>>   do things here...
>>   if moonIsGreenCheese() {
>>     err := makeASubroutineCall(...)
>>     if err == nil && someCondition {
>>       err = someOtherError
>>     }
>>     if err != nil {
>>       return ..., err
>>     }
>>   }
>>   do more things
>> }
>>
>> And then another person comes along, and now it's this:
>>
>> func Foo(...) ..., error {
>>   do things here...
>>   if moonIsGreenCheese() {
>>     err := makeASubroutineCall(...)
>>     if err == nil && someCondition {
>>       err = someOtherError
>>     }
>>   } else if moonIsRedCheese() {
>>     err := makeDifferentCall(...)
>>     if err == nil && someOtherCondition {
>>       err = yetAnotherError
>>     }
>>   }
>>   if err != nil {
>>     return ..., err
>>   }
>>   do more things
>> }
>>
>> Do you see the bug? Wouldn't you be glad if you had a test hanging around 
>> to catch it?
>>
>> On Thu, Jan 28, 2021 at 12:22 PM Szczepan Faber <szcz...@gmail.com> 
>> wrote:
>>
>>> Good question and useful discussion!
>>>
>>> What is Go community guidance on the _value_ of unit testing the `if err 
>>> i= nil { return err }` idiom?
>>>
>>> To make the question a little more precise, let's consider the code 
>>> snippet in the first email in this thread. Let's assume that I already have 
>>> coverage for Foo() function happy path. Does it make sense to increase the 
>>> code complexity (adding mocks) in order to achieve a higher test coverage 
>>> (covering 'return err' line)? Would that additional coverage be useful 
>>> given that 'return err' has no complexity and Go has the compiler/linter?
>>>
>>> Full disclosure: I'm biased to avoid unit testing those idioms by 
>>> default. However, I'm very curious what's the community guidance, any 
>>> documents/links I can read, any reference codebases?
>>>
>>> Thank you all!
>>>
>>> On Tuesday, December 8, 2020 at 4:39:05 AM UTC-6 
>>> axel.wa...@googlemail.com wrote:
>>>
>>>> Hi,
>>>>
>>>> On Tue, Dec 8, 2020 at 1:19 AM 'Charles Hathaway' via golang-nuts <
>>>> golan...@googlegroups.com> wrote:
>>>>
>>>>> Hi all,
>>>>>
>>>>> I'm looking for a good study/quantitative measure of how well-written 
>>>>> Go code looks compared to other languages, such as Java, when it comes to 
>>>>> test coverage. In particular, how handling errors may reduce the 
>>>>> percentage 
>>>>> of code covered by tests in Go relative to other languages.
>>>>>
>>>>> For example, in this code snippet:
>>>>>
>>>>> func Foo() error {
>>>>>   // do some stuff that actually adds value
>>>>>   if err := somelib.Bar(); err != nil {
>>>>>     // triggering the error case in Bar is hard, i.e. requires 
>>>>> simulating network troubles
>>>>>     // or causing a file write to fail, but we don't do anything with 
>>>>> result besides
>>>>>     // return it. Testing it by adding an interface or wrapper isn't 
>>>>> worth the effort
>>>>>     // and the only impact is really reported test coverage.
>>>>>     return err
>>>>>   }
>>>>>   // do more stuff
>>>>>   return nil
>>>>> }
>>>>>
>>>>> In Java, you would just add 'throws SomeException' to your method 
>>>>> declaration. The effect is that we have one line in the Go code which is 
>>>>> not easily covered by a test, whereas Java does not report that untested 
>>>>> case because the return path is not visible in the code.
>>>>>
>>>>> The result is that otherwise equivalent code, we will report different 
>>>>> code coverage values, with Go being slightly lower. I'm just looking for 
>>>>> something written on that topic that can give us a notion of how much of 
>>>>> a 
>>>>> difference we might expect.
>>>>>
>>>>
>>>> I don't think there is as much of a difference as you think.
>>>>
>>>> You seem to be considering the `throws SomeException` to not impact 
>>>> coverage - but that's not true. It's code you add for error handling and 
>>>> that code is not hit, unless your test actually triggers that exception - 
>>>> just as the code you add for error handling in Go isn't hit. So if you 
>>>> don't count `throws SomeException` as code to be covered in java, you also 
>>>> shouldn't count `if err i= nil { return err }` as code to be covered in 
>>>> Go. 
>>>> So the semantic difference really comes down to a single `throws 
>>>> SomeException` line being able to cover *multiple* branches with the same 
>>>> exception type. It's a difference, but it should be small in practice.
>>>>
>>>> But really, I think what this comes down to is that line-coverage - or, 
>>>> what's actually measured and then projected down to lines, 
>>>> "instruction-coverage" - just isn't a super meaningful measure in this 
>>>> context. More interesting would be branch- or path-coverage - and that 
>>>> would be exactly the same in both cases. Every point where a 
>>>> `SomeException` *could* be thrown would branch off a separate path, just 
>>>> as 
>>>> every `if err != nil` in your Go code. And in both languages they are 
>>>> covered iff you write a test-case that triggers that error condition.
>>>>
>>>> So… I'm sorry that I can't really provide a quantitative, meaningful 
>>>> answer to your question. I don't know what relative difference there would 
>>>> be in line-coverage for Go vs. Java in a case like that. But your question 
>>>> sounds as if you would like to use line-coverage as a metric (maybe even 
>>>> in 
>>>> CI *shudder*) to determine whether you tested enough. And the point I'm 
>>>> trying to make is that I think that goal is fallacious :) If you need a 
>>>> coverage-metric, use branch- or path-coverage, which won't have that 
>>>> difference. But really, coverage reports are IMO most useful if inspected 
>>>> manually, to choose where to invest further tests. As a metric, it just is 
>>>> too unreliable.
>>>>
>>>> https://en.wikipedia.org/wiki/Goodhart%27s_law
>>>>  
>>>>
>>>>>
>>>>> Thanks,
>>>>>   Charles
>>>>>
>>>>> -- 
>>>>> You received this message because you are subscribed to the Google 
>>>>> Groups "golang-nuts" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send 
>>>>> an email to golang-nuts...@googlegroups.com.
>>>>> To view this discussion on the web visit 
>>>>> https://groups.google.com/d/msgid/golang-nuts/6b48ed73-1963-482e-aff0-b91f3aa6a2aen%40googlegroups.com
>>>>>  
>>>>> <https://groups.google.com/d/msgid/golang-nuts/6b48ed73-1963-482e-aff0-b91f3aa6a2aen%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>> .
>>>>>
>>>> -- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "golang-nuts" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to golang-nuts...@googlegroups.com.
>>> To view this discussion on the web visit 
>>> https://groups.google.com/d/msgid/golang-nuts/1acd3558-93ca-494c-b639-af6f694a1fcfn%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/golang-nuts/1acd3558-93ca-494c-b639-af6f694a1fcfn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>> -- 
>>
> You received this message because you are subscribed to a topic in the 
>> Google Groups "golang-nuts" group.
>> To unsubscribe from this topic, visit 
>> https://groups.google.com/d/topic/golang-nuts/YC6A4DQEyl8/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to 
>> golang-nuts...@googlegroups.com.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/golang-nuts/CA%2BYjuxtWuNbSePd855tiJRT7fZF3%3DoFz1ve2PnP8St%3DkbteCEg%40mail.gmail.com
>>  
>> <https://groups.google.com/d/msgid/golang-nuts/CA%2BYjuxtWuNbSePd855tiJRT7fZF3%3DoFz1ve2PnP8St%3DkbteCEg%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/eee18750-f9de-4ed5-b98b-7e36357b51den%40googlegroups.com.

Reply via email to