In this blog post we will concern ourselves with unit testing of classic 3-layer business applications. We will assume that all business logic lives in services and components, that these services operate on entities that are stored and retrieved from relational database, and that these entities doesn’t contain any logic. Moreover we assume usage of DTO (Data Transfer Object) to pass data between GUI and application services.
Dependency Injection is indispensable when it comes to unit testing of modern business applications. Without DI you are forced to write slow and difficult to maintain integration tests instead of unit tests. If you don’t know what DI is or if you don’t used it before please read articles on Wikipedia and Martin Fowler site, and return here after you are comfortable with both idea and usage of DI.
Now when we are ready to start, we will follow Confucius advice:
By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest.
UserService implements following business requirements:
- Users forgot their passwords from time to time, application should provide a way to reset forgotten passwords.
- To reset their passwords users must provide email address they use to login to our system.
- If provided email address does not belong to any user, application should do nothing. Otherwise application should generate unique password reset token and send to provided email address message with link to reset password form. Link should contain reset password token. In both cases application should show to user success message.
- Password reset tokens should be unique. Tokens should be hard to guess or enumerate (no numbers here). Token may be used only once to reset password. If we want to reset password again we need a new token. Token is valid for 24 hours starting from the date it was created, after 24 hours token cannot be used to change password.
- When user open reset password link in her browser it should be presented with a form that allows to enter a new password. After clicking OK, application should validate token used in link, and if it is still valid application should change user password and make token invalid. Then application should send password change confirmation message to user. In case of expired token application should show warning to user.
WARNING Before implementing real password reset feature please read Everything you ever wanted to know about building a secure password reset feature.
Now when we understand business requirements we may attempt to implement
UserService operates on the following
And requires four other components to work, namely:
DateTimeProvider dependency was introduced solely for the purpose
of easier unit testing, as we will find out later.
UserService follows principles of DI, component advertises
all dependencies it needs as constructor parameters.
DI containers may then use constructor based dependency injection to
provide implementations of these dependencies.
Right now we have only implemented
other dependencies are not implemented yet.
Nevertheless with usage of stubs and
mocks we may test
UserService implementation right now.
Writing tests for
By convention we should put tests for
class. For example tests for
UserServiceImpl should be put
When you use Maven you should put your test class in
the same package that contains tested component.
is part of
io.mc.letsmock.demo package, so
should also belong to
With Maven the only difference between application code and test code is the
directory in which code resides. Application code will be in
directory and test code will be in
convention used with e.g. Ant is to put applicaiton code into
mycompany.productA.xx.yy package and test code
The important thing here is that team should choose one particular convention how to name tests and where to put test classes and stick to it. If you use Maven I strongly encourage using conventions that I described above.
After reading requirements we come to conclusion that we need the following
test cases to be sure that
startResetPasswordProcess method works:
- When we couldn’t find
Userwith specified email address, component should do not nothing, in particular is should not crash
- When there is
Userwith specified email address, component should set
Userinstance to respectively newly generated token, and
LocalDateTimeinstance that represent point in time 24 hours later than now.
- When there is
Userwith specified email address, component should send message with token to user using
Notice that each of these test cases test only single thing, this is very important if we want to have clean and independent tests. As method should do only one thing, test should test only one “outcome” of a method. When you gain more experience you may want to relax this rule, but if you just started unit testing you should stick with it for some time.
There are two schools when it comes to naming test methods, first school teaches that test name should consists of three parts:
scenario is the thing that we want to test, this in most cases
will be the name of the method that we want to test.
conditions describe the state of program that tested method
may expect when it is called.
outcome is the state of the program
that we expect after tested method returns.
To give you a feeling how this naming scheme works here
are some dummy tests for Java
The second school took inspiration for thier naming scheme from
This school advices that
test names should consists of full sentences that describe both
conditions and outcome of tested method.
+ operator tests following this
scheme looks like:
As you can see test names generated using this approach can be quite verbose at times. Verbosity of this schema is it great advantage because the main purpose of test name is to tell you what exactly is not working when given test fails.
I prefer first school with scenario/conditions/outcome division of test name, so I will use it exclusively in the rest of this post.
startResetPasswordProcess we should create three tests:
These test names are not as descriptive as they may be, but are close to what you may expect in average enterprise application.
Anatomy of test method
Our first test will check that
startResetPasswordProcess won’t throw
exceptions when called with email address of non-existing user.
But to test
UserServiceImpl class we must create it’s instance and this
will require providing all necessary dependencies.
Fortunately we may use Mockito library to
dummy implementations of
and others. By default these dummy implementations do nothing and
are similar to handcrafted test stubs like:
But we will soon see that we can instruct Mockito to return values from these stubs or to check if a given method was called on dummy object.
Below you can see our first test code:
Before we dig into details let’s look at this test from high level point of view. Almost every test method can be divided into three parts called Arrange-Act-Assert or Given-When-Then. I used comments to signify when each of these parts start. Each of these parts has different purpose. In Arrange part we must create instance of tested component and all necessary test data and also we must set up Mockito mocks. If we may compare test method to theater play, then Arrange is like preparing scene, costumes and lights.
The first four lines of our test Arrange section are responsible for
creating dummy implementations of
Then we set up one of our stub objects:
Here we instruct Mockito that dummy implementation generated for
null when called with
But to be honest these lines are redundant because
method stubs generated by Mockito by default return
reference types, default values for primitives and empty collections
for methods returning collections.
Still I leave them because they
make purpose of our test more evident.
After Arrange section we have an Act section. Again (so many AAAAs) returning to our theater play analogy (another A, no pun intended) Act section is like the actual play, we invoke tested component and let the code be alive. Act part is usually very short, in most cases it consists of single line of code:
The last section in the test method is Assert when we want to check results produced by tested code. In our test we check two assumptions:
- Invocation of
startResetPasswordProcessdoes not throw any exceptions. This is tested implicitly by JUnit - test fails when test method throwns exception. Since our test passes we are certain that
startResetPasswordProcessdoesn’t throw any.
- We want to be certain that no notification was send to provided email address
so we asses with the help of Mockit that none of the methods on
Mockito verification syntax is a bit unintuitive, so let’s take a closer look at one of our assertions:
verify to tell Mockit that we want to preform verification.
never() means that we expect that method was not called on dummy object.
Then we specify method that should not be called,
in our case
any() as method parameter to tell Mockito that method
should just not be called, and that we don’t care about parameters
that was passed to it. Mockito is quite flexible here
and we may for example verify that method should not be called with given
set of parameters but must be called with another. We may also replace
never() with one of several predicates like e.g.
times(2) to assert that
method was called twice.
Testing with assertions
Our first test assured us that
startResetPasswordProcess works correctly
with email address of unknown user. Now it is time to check if
it also works correctly given email addresses of existing user.
Our second test will check if given valid email address
generates a new token for
User and sets it expiry date correctly.
We also will check that user password is not altered in any way by
starting reset password process (it should change only when we finish
password change process).
Here is our second test code:
Arrange section of our second test does not differ much from Arrange
section of our first test, except that we added code for creation
User instance. Notice that we populate
User with carefully
chosen data that will allow us to test
implementation easily e.g. we set both
Then we instruct Mockito to return our
User instance when we
UserRepository for user with
email@example.com email address:
We should strive to make our unit tests as much deterministic as possible.
Unit test that sometimes fails without a reason is burden rather than
a benefit, and should be either fixed or removed.
To make unit more robust we should avoid depending on external input
like information about current time. To facilitate that I decided to
DateTimeProvider component with sole purpose of making
unit tests more predictable. With small help of Mockito we are now
masters of time:
After all this preparations, we are now free to invoke
and check it’s outcome. In unit tests we mainly use assertions to check
correctness of tested code. JUnit already comes with handy assertion library
that we may use like this:
When JUnit assertion fails it throws
AssertionError, test fails and
we get error similar to:
Unfortunately error messages generated by JUnit assertions are not always the best way to find out what went wrong with failing tests. JUnit assertions are also cumbersome to use at times. From these and other reasons I prefer to use assertions from assertJ library and I used them when I wrote our second test:
Here we check three things:
- New password reset token was generated and assigned to
- Expiry date of password reset token was set correctly (token should be valid for the next 24 hours)
- Current user password was not changed
Notice also that we use
withFailMessage to provide additional
information in case our third assertion fails.
withFailMessage we would get following error:
withFailMessage we get:
Right now both of our tests pass, but we see a lot of code duplication
between them. Now it is a good time to extract common parts of both
setUp method and to create some fields in our test class:
After refactoring both dependencies and tested components are
now stored in fields of test class.
JUnit calls any method annotated by
@Before before executing each
of test methods contained in test class.
setUp method suitable place to initialize fields
that will be used by many tests.
Now you may be tempted to move initialization code to the test class constructor, but don’t do that. Unit tests should be independent of each other. One of the worst sins when writing unit tests is to write a test that depends on some data created by other test methods. Such incorrect test may pass when we ran all tests but will fail when run it alone. This is one of the worst things that may happen when writing unit tests, and clearly shows that we do something wrong. Instead every test should create it’s own test data and should use fresh Mockito stubs. Later you will appreciate this independence of tests when you will try to run tests in parallel.
Unit tests often require some dummy data, instead of creating the
same object again and again in various tests we should group them
into library of test objects. Such library of test data is often called
a fixture. Since I expected that we will need
User instance in other
test, I extracted code that created dummy user into
When reading about unit testing you may encounter terms fake, mock and stub.
Fake is any object that is used only by test code, fake may be implemented as
concrete class like
UserRepositoryStub or as anonymous class generated at runtime.
In this last category we find all dummy implementations generated by Mockito.
Fakes can be divided into two groups stubs and mocks. Form practical point of
view we use mock to test interactions, and stubs to provide dummy data or
do-nothing implementation. We used
NotificationService as a mock in our first
test because we used it to assert that no interaction took place (no message was
send to user). On the other hand all dummy implementations generated
by Mockito for
DateTimeProvider etc. are examples of stubs.
More information about difference between mocks and stubs can be found in Martin Fowler article Mocks aren’t stubs.
When unit testing it is important to test all execution paths in our code. Sometimes we may be convinced that we covered all corner cases only to find out (usually in production) that we overlooked testing some obscure conditions. In such situation after fixing bug, we should add missing test. But we could do better, almost any popular Java IDE can measure and show us code coverage of tested component. IDE can usually highlight in red lines that were not tested, for example here how it looks like in IntelliJ: Greenish bar next to line number tells us that line of code was executed when running unit tests (being executed doesn’t automatically mean that the line of code is well tested, it is only a heuristic). On the other hand red bar tells that lines of code was not reached by tests and certainly is not tested.
When we are at it, you may heard that the higher code coverage the better. Having 100% code coverage is impossible in any reasonable sized enterprise application. There is an ongoing debate about how much code coverage is enough. For me code coverage is just a tool that I use to check that I tested all execution paths in code and nothing more.
It’s your turn now
Right now we have only two tests for our
of course it is not enough to assure us that all of the business requirements
were fulfilled. When I tested
UserServiceImpl I wrote seven more tests:
and only now I am certain that
UserServiceImpl works correctly.
You may find source code for all these tests (with other goodies) HERE. But before you look at what I wrote, please try to implement these tests yourself and then compare your code with mine. I am certain that you will learn more this way.
Thanks for reading. If you liked this post please start my Github repository. And see you soon again!