Advice on unit testing
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
A unit test NEVER uses:
*a database
*an app server (or server of any kind)
*file/network I/O or file system;
*another application;
*the console (System.out, System.err, etc.)
*logging
*most other classes (exceptions include DTO's, *String, Integer, mocks and maybe a few others).
The problem is I have classes in my project that do things like:
*copy files
*use other classes (eg/I have a helper class that is used by multiple other methods in other classes).
*use other applications (it's an OSGI project that uses the 'pax' application)
and so on.....
My conundrum is this:
The article and original statement seem contradictory. How can I have 100% coverage, if I am NEVER supposed to use the resources in the list (files etc..)? For example, how can I test a file copier class, if I am never supposed to use files in a unit test?
-
1 -
-
Number of slices to send:Optional 'thank-you' note:
-
-
What a unit test should indeed not do is anything time-consuming, because you want to be able to run unit tests frequently.
-
3 -
-
Number of slices to send:Optional 'thank-you' note:
-
-
Billy Sclater wrote:
The article and original statement seem contradictory. How can I have 100% coverage, if I am NEVER supposed to use the resources in the list (files etc..)? For example, how can I test a file copier class, if I am never supposed to use files in a unit test?
There are solutions for everything. An OutputStream can be mocked, a database can be simulated (DbUnit)
A Unit test will never use real other classes. All dependencies are mocked. If a unit test uses other classes, it's not a unit test, it's an integration test. You should do automated integration and functional tests, they are just not called unit tests.
What a unit test should never do, is be aimed at providing 100% coverage. You should unit test functionality, not getters and setters. If you use a tool like Cobertura, it should give you 50/70 % coverage, but it can be more or less, depending on the application. Coverage is a good way to see if your project is tested, mostly because of the difference between 0-10% coverage and +- 50% coverage. But you shouldn't consider 100% as some holy grail of testing, because too high coverage generally means your tests are not testing the right things.
Oracle Certified Professional: Java SE 6 Programmer && Oracle Certified Expert: (JEE 6 Web Component Developer && JEE 6 EJB Developer)
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
Is this appropriate? As by definition I now have an integration test - more than one class in the test, not a unit test.
-
1 -
-
Number of slices to send:Optional 'thank-you' note:
-
-
The solution to this is not to view tests as something you write at the end of development. Tests need to be at the forefront of your mind as you are designing your application, and this will result in a design that is easy to test. The Test Driven Development methodology even goes as far as writing tests [i]before[/] the implementation code.
There are a lot of things that are important when designing testable code, but a few of the most important things (in my opinion) are:
* Write Interfaces for your domain objects. This makes it easier to mock them.
* Use dependency injection. If your class creates concrete instances of objects itself then you cannot mock those objects and have no control over them. On top of this, program to Interfaces, not implementations.
* Use a good mocking framework in your tests. I use Mockito, but there are others available.
* Keep your classes focussed. If a class has a lot of incoherent jobs then it should probably be split into several classes. That will make testing easier as each class will have fewer dependencies.
-
1 -
-
Number of slices to send:Optional 'thank-you' note:
-
-
Dieter Quickfend wrote:A Unit test will never use real other classes. All dependencies are mocked. If a unit test uses other classes, it's not a unit test, it's an integration test
That suggests that the hard definition of a "Unit" in a Unit Test is a single class.
I think the definition of a Unit is a bit more flexible than that. I see it as a logical unit within your application which may consist of a single class or multiple classes. For example, the product I develop at work has to publish messages on a Tibco bus. So I have a "unit" that takes a message in the format of our domain model and translates that into a Tibco format to be sent down the wire. My Unit Tests here will be to ensure that if I put in domain object A then I get out Tibco message B. As it happens there are a handful of classes and things in the middle that actually make that happen but I do not necessarily have any unit tests for those individually. My "unit" in this case is the code that translates the message from format A to format B. I don't really care at this point how many classes are involved.
The purpose of having unit tests in your code is to give you the ability to refactor safely. In my example above, I am now free to completely change how I implement building those messages but as long as my tests pass then I know I'm building those messages correctly. If I had unit tests around each individual class in there with Mocks for every interaction with other classes then I would be encasing my code in concrete. I'm now writing tests to specify the implementation. I don't want that. I want tests to specify the functionality.
Tim Driven Development | Test until the fear goes away
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
When writing a unit test for this I don't really want it to actually touch the filesystem because it's slow and there might be permission problems on the current platform which then leads to flakey tests which fail sometimes and not others. So in this case you can do as Dieter suggests and work with an OutputStream (mockable interface) rather than File (un-mockable final class). Or I think there is some library available that gives you an in-memory representation of a file system which allows you to actually read and write quickly in your tests. (I can't remember what it's called, sorry)
You're also going to need an Integration Test to verify that your code does actually write and read the file to and from the real filesystem. For this you're going to need to make sure the environment is set up correctly with directories created with correct user permissions granted on them.
Unit tests need to be quick as those are the ones you run repeatedly all day long on your local development environment. Integration Tests are run less frequently, once a day perhaps, and are run in an environment that is as close to production as is feasibly possible.
Tim Driven Development | Test until the fear goes away
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
Categorizing the things in your list:
I think an integration tests should to these (they are still in JUnit, but a different type of test)
*a database
*an app server (or server of any kind)
*network I/O
*another application;
A unit test
*the console (System.out, System.err, etc.) - if you want to test the output, mock it. if there just happens to be output, ignore it
*logging
*most other classes (exceptions include DTO's, *String, Integer, mocks and maybe a few others) - calling small classes is fine. Calling classes with significant logic is a sign that you should mock instead of calling directly.
Gray area (I have written "unit tests" to check XML files, provide "golden master" copies of data, test configuration, test for usability WCAG/Section 508, etc. As long as they are fast, I see no problem with this.).
*File I/O
[OCP 21 book] | [OCP 17 book] | [OCP 11 book] | [OCA 8 book] [OCP 8 book] [Practice tests book] [Blog] [JavaRanch FAQ] [How To Ask Questions] [Book Promos]
Other Certs: SCEA Part 1, Part 2 & 3, Core Spring 3, TOGAF part 1 and part 2
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
Tim Cooke wrote:
I think the definition of a Unit is a bit more flexible than that. I see it as a logical unit within your application which may consist of a single class or multiple classes. For example, the product I develop at work has to publish messages on a Tibco bus. So I have a "unit" that takes a message in the format of our domain model and translates that into a Tibco format to be sent down the wire. My Unit Tests here will be to ensure that if I put in domain object A then I get out Tibco message B. As it happens there are a handful of classes and things in the middle that actually make that happen but I do not necessarily have any unit tests for those individually. My "unit" in this case is the code that translates the message from format A to format B. I don't really care at this point how many classes are involved.
The purpose of having unit tests in your code is to give you the ability to refactor safely. In my example above, I am now free to completely change how I implement building those messages but as long as my tests pass then I know I'm building those messages correctly. If I had unit tests around each individual class in there with Mocks for every interaction with other classes then I would be encasing my code in concrete. I'm now writing tests to specify the implementation. I don't want that. I want tests to specify the functionality.
I believe you are thinking of an integration test. Although I do believe integration tests are the most bang-for-buck tests I usually write, it is also worth writing unit tests to see if every step in your current implementation functions as expected. It is quite normal that, in case of a change of implementation, you would have to change or rewrite your unit tests for that change. After all, if your implementation changes, all your assumptions become invalid.
This is all a matter of interpretation, I guess, but this is how I see it:
- Unit tests are tests that test a logical unit (an exposed (non-private) operation inside a class), isolated from all other exposed logic. All dependencies are mocked out.
- Integration tests are tests that test a logical unit which depends on functionality from other units, along with its dependent units (exposed in another class and thus reusable by other units). These dependencies should also have unit tests.
Think about it. A low-level method returning one of three strings for a certain parameter is changed and will now return only two. In a business method 5 levels of abstraction above this method, you will never test all of these possibilities. The only place this is done is the unit test for the low-level method. If you don't have a unit test for the low-level method, the cases in which the third string must be returned for the business method to behave correctly will no longer function.
Oracle Certified Professional: Java SE 6 Programmer && Oracle Certified Expert: (JEE 6 Web Component Developer && JEE 6 EJB Developer)
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
Dieter Quickfend wrote:I believe you are thinking of an integration test.
Not in this example no. The "Unit" in my test is a collection of classes that work together to perform a single function. It would become an Integration Test if I were to actually send that message over the wire using Tibco to some other machine or process. But in this case I am not, so I'd still call it a Unit Test.
I think of "integration" as an interaction with some system outside of the application. Things like a database, a web service, an external message system, pretty much anything that requires a separate system to be up in order to operate. I do not necessarily consider the interaction between two classes an integration point unless it represents a communication between two logically different parts of the application, although in that case I might term it more of an "end to end" test.
Call it whatever you like really. If it works for you then go with it.
Tim Driven Development | Test until the fear goes away
-
-
Number of slices to send:Optional 'thank-you' note:
-
-
The important thing from a Unit Test point of view is that the created type must not have dependencies of its own other than things that are passed in through the public API of the creating class. Any use of static global variables or methods (with Singletons often being an example) can lead to code that is difficult or impossible to test.
| It runs on an internal combustion engine. This ad does not: The new gardening playing cards kickstarter is now live! https://www.kickstarter.com/projects/paulwheaton/garden-cards |











