Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Effective Unit Test Coverage (and what should actually be tested?)

I see there's been several questions based off this topic, but I have some specific examples that I'm struggling with. I hope someone can help me here, as I'm new to test driven development and unit testing in general.

(I'm not sure if it matters but I am using C# with .NET framework 4.0 and using Microsoft's built in testing framework)

First off, is it worth testing very simple blocks of code? For example, for one of my classes I have a Fill method which accepts 10 parameters, and sets the values for 10 properties in the class based off of those parameters. The fill code really is just a series of value setting statements. I've read that a good unit test is supposed to only assert one thing, but it seems to me in order to check that all those values have been set correctly I would have to assert 10 things. So either I can not test it at all, trusting that the code is simple enough, or write 10 different unit tests to check each property. Are any of these correct?

Second example. I have a block of code that makes several calls to a class's private methods and finally calls a web service to fire off an email to a user. I understand that I should inject a mock web service for the email service so that I'm not testing multiple classes, but how should I test that all those private methods and that email call have been made?

Third example is similar to the last. My class structure looks something likes:

Controller --- depends on ---> IWidget(some business object) && IDataProvider DataProvider(Implements IDataProvider) --- depends on ---> WebService WebService --- directly makes calls ---> Database

Right now I have unit tests for the controller (injecting mock Widgets and DataProviders). So that's all good. I also have unit tests for Widget with no problems.

The trouble comes down to the DataProvider and the WebService. The WebService in this particular instance does nothing but passes through the requests of the dataprovider and passes back the data (due to physical architecture restraints).

I have trouble unit testing the DataProvider because I'm not sure how to go about injecting a mock web service. I'm also unsure if unit testing the DataProvider is worth while, because I'd wind up having to code in a lot of mocked up datasets just to test values. Similarly I'm unsure how to go about unit testing the web service because in this instance the WebService's main function depends on the database. Once again, is this worth testing? What if the WebService did more besides just serve as a pass through but was still dependent on the database?

I'd greatly appreciate any advice that anyone can offer in this regard. Thanks very much.

like image 994
Landon Avatar asked Apr 12 '11 20:04

Landon


2 Answers

I've read that a good unit test is supposed to only assert one thing, but it seems to me in order to check that all those values have been set correctly I would have to assert 10 things. So either I can not test it at all, trusting that the code is simple enough, or write 10 different unit tests to check each property.

I would say a good unit test should test one story or use case scenario, which can indeed involve multiple assertions.

If the method involves 10 assignments, there is indeed a chance that some are messed up, names are misspelled etc., so a unit test provides value for its (small) price.

I have a block of code that makes several calls to a class's private methods and finally calls a web service to fire off an email to a user. I understand that I should inject a mock web service for the email service so that I'm not testing multiple classes, but how should I test that all those private methods and that email call have been made?

This is called sensing, i.e. sensing the results / side effects of the call you made. The answer depends on what those private methods actually do. If they assemble some data which in the end goes into the mail message, it can be tested via the message contents. If they change the internal state of the object in a way that can be detected from outside, you can verify it. If none of these is true, it might even be the case that those private methods would be better moved into a separate class' public interface where they can be properly unit tested.

I have trouble unit testing the DataProvider because I'm not sure how to go about injecting a mock web service. I'm also unsure if unit testing the DataProvider is worth while, because I'd wind up having to code in a lot of mocked up datasets just to test values.

I would not necessarily want to unit test such classes which are in fact wrappers to external components. If there is only the absolute minimal amount of logic there, i.e. all (or the most possible) code worth testing was moved out to other, unit testable, classes, IMO it can be OK to leave the testing of these classes to the integration/functional tests, which exercise the whole (sub)system connected to a real DB, web service etc.

like image 81
Péter Török Avatar answered Nov 05 '22 19:11

Péter Török


Ad First)
You could argue that you are not really providing any functionality if you are only doing setting statements. But if you have a boss who into the code coverage thing, not testing such a method would get you in trouble. And yes, you should only assert on one thing, but IMHO asserting on each property is set correctly by a method, then it only counts as one assert -- regardless of how many assert statements is in your test method. Sometimes I even extract all assertments into a private method to illustrate this

Ad Second) If you have the need to test private methods, that an indication that you'd probably be better off extracting those.

Ad Third)
I have avoided this issue by wrapping all webservice call in a gateway class. The interface of this gateway can be mocked and injected. I try to do this with all external communication if possible.

Updated:
Oops... messed up with the numbering. Fixed it now

like image 28
Morten Avatar answered Nov 05 '22 17:11

Morten