Recently I had the oppertunity to explain some students about what unit testing was. I started off with the question of “What does unit testing mean?”. They gave different types of answers. One of them talked about the smallest piece of code. And even though he is ‘right’. I asked him to apply this knowledge to his current code where he said “But I don’t want to test my get/set methods, that is useless!”. And so, our definition of ‘unit testing’ became unclear again.
So what is a unit test? According to this article on wikipedia “A unit is the smallest testable part of an application”. But what does that mean? What is the ‘smallest testable part’? Do you need to test get/set methods? Do you need to test assigning values? Yes and no.
Even though the smallest testable part is related to lines of code, I believe it also is related to behavior. I think a better description for unit testing would be to test the smallest piece of behavior of an application. Most of the time you probably will be testing very little pieces of code with it. As long as there is some behavior (context) you want to test.
So do get/set methods fall into this category? Depends… Get/Set methods in themselves are, without context, useless. Returning a value or setting a value is not much worth of testing. However, if you test a method that calls a get/set method and does something with it, that is another story. When code is executed within a certain context, it is perfectly valid to unit test it. An example: getTotalPrice that just returns a value “price” is not worth to be testing. A getTotalPrice that does some calculation is a good candidate to test!
With software development, it is the expected behavior of the software that matters. I compare this with designing interfaces (seperating what and how), where you’re busy thinking what you would expect from a certain object when using it. The behavior of the software; how it presents itself and how the user can interact with it (the how), is different from how the software realizes this behavior (the what). When thinking in terms of behavior when writing code, we force ourselves to think of what the software should be doing, and not *how* it should be coded to do that. Test Driven Development is a way of forcing you to write code that has a good design. It clearly a functional (behavior) point of view.
With that said, a unit test should aim to test the smallest possible piece of behavior. When a total price is shown. There are a few steps taken before it is shown on the screen. One of the important behaviors is that the totalprice is calculated somewhere. Testing the calculation of the totalprice is a unit test. This is a State based test. (Does the class give expected output X when using input Y?)
The totalPrice is calculated from certain input. The output is shown on the screen.
A controller class is putting input and output together, making sure the totalPrice is being calculated and pushed to the view. Making sure that the controller uses its collaborators to do that is also a unit test. This is called a collaboration test. (Does my class call the collaborators that I expect, with the parameters I expect?, yes you do that by Mocking)
Even though with these two tests, you still miss an important type of test. You still need to test if your interfaces behave as they say they should. Should you test interfaces of external (3rd party) API’s? Probably not. Unless you have a reason to distrust your supplier of the API. Testing interfaces are called Contract Tests and look a lot like State based tests. These tests are making sure your expectations about the interfaces are validated! If you don’t do that you will get defects, even though you test states and behaviors.
Last but not least. It is useful to test broader pieces of behavior. For example, if you use Spring to bootstrap your webapp. You probably need to make sure everything is autowired correctly. That is an integration test. There are multiple types of integration tests. An often used integration test is to connect to a database, put data in, fire up some methods and test their behavior. All of them are integration tests. Hence, even testing whole pages (end-to-end tests) using Selenium, those are also integration tests, although at a much higher level.
In time I will blog more about the type of unit tests.