I was reviewing some code for a colleague and came across a test in the unit test class that looked this:
// setup
Foo f = ...
FooToBarConverter ftb = ...
Bar b = ftb.Convert(f); // It is easier to create a Bar by converting it from a Foo than making one 'from scratch'
// test
systemUnderTest.DoSomething(bar);
// assert
Assert.IsTrue(...)
Clearly this is an integration test as it is testing the FooToBarConverter as well as the system under test as it is the only test that covers the DoSomething() method. I suggested moving this test to an integration test solution, however this reduces the code coverage of the unit tests. We are aiming for 100% unit test code coverage (and yes - I know that 100% coverage is a means to an end not the end itself, and 100% covered code is not necessarily 100% correct code).
Is there a reason for creating unit tests to bring the coverage back up if we move the integration test out?
Or are we aiming for the wrong thing with 100% unit test coverage? Should we be aiming for 100% coverage with the combination all our tests (or even aiming for 100% at all)?
Thank you.
EDIT/UPDATE:
This is not a question about how to unit test the system under test properly (I know the reasons that this is not a unit test, and I know how to properly convert it into a unit test), nor is it a question on coverage on FooToBarConverter. I want opinions on code coverage on the system under test: are integration tests on the system under test sufficient? or should there also be unit tests?
Going back to code coverage, code coverage is nothing more but checking which lines of code were executed during a test run. A single integration test might be hitting a great amount of lines of code, giving a big boost of code coverage.
Unit Testing is a kind of white box testing, whereas Integration Testing is a kind of black-box testing. For Unit Testing, accessibility of code is required, as it tests the written code, while for Integration Testing, access to code is not required, since it tests the interactions and interfaces between modules.
Unit tests help to ensure functionality and provide a means of verification for refactoring efforts. Code coverage is a measurement of the amount of code that is run by unit tests - either lines, branches, or methods.
Execute Test Case: After the generation of the test case and the test data, test cases are executed. Defect Reporting: Defects in the system are detected. Regression Testing: It is carried out to test the side effects of the testing process.
I think the answer here is "it depends".
If you have full unit test coverage on the FooToBarConverter
class then probably you are OK just with the integration test of systemUnderTest
because you can say with confidence that the real FooToBarConverter
behaves as expected in this context and therefore does not incorrectly influence the result of the test.
On the other hand, it's unclear specifically what this test is checking for - are you examining the behaviour of systemUnderTest
when given a valid FooToBarConverter
, or some other expected side effect within systemUnderTest
to which FooToBarConverter
is a purely coincidental actor? (i.e are you sure that this isn't an indirect test of bar
?)
Now personally I would recommend that you also do a proper, "pure" unit test (using a mock or stub of FooToBarConverter
) for systemUnderTest
because
it will make regressions easier to manage; suppose that in the future some change to FooToBarConverter
makes its unit tests fail - they will quite possibly also therefore make this integration test fail. That could be confusing for someone looking at failed tests and not knowing that the integration test failure can be ignored and that only the FooToBarConverter tests need to be fixed. It's a small thing, I know, but it might save 5 important minutes some day :)
How do you test the negative cases (behaviour of systemUnderTest
when given a broken/invalid/null FooToBarConverter
)? Since you'll probably have to write unit tests with stubs/mocks for these kind of cases anyway, you might as well have a unit test for the good case in the same project/test class as well, it's much clearer - otherwise you have to aggregate code coverage across both unit test and integration test projects to verify that systemUnderTest
is fully covered...
Also, don't worry about 100% code coverage; it's nice to have but in practice it's rare to see it. I don't mean this as a sop to good design practices either; the simple reality is that no design is 100% perfect and therefore it's to be expected that there are times when you just don't have the time/resource/will to refactor your classes to allow every single dependency to be injected, or to be able to use interfaces for every inter-seam interaction, etc.
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With