Introduction to Test-Driven-Development

What is Test Driven Development?

In order to explain why TDD is so useful and why you should incorporate it into your day-to-day job of a software developer I first need to explain what it is and provide an example of how it works.

The purpose of TDD is to first write code that works and later apply the principles of Clean Code to improve the readability, while assuring that your code still works as expected.

This process is very iterative and allows the code to provide feedback between changes.

More specifically this is the rhythm which serves as the heart and sole of TDD. Everything you learn afterwards like the principles (discussed later) will help master TDD. Practise the rhythm first and master it, then later once you’re confident and the time is write you can go onto mastering TDD by learning the principles.

To achieve clean code that works we must drive development with automated tests. This leans back onto the rhythm of TDD.

The rhythm of TDD embraces that we must design with intent of communicating with others and further to eliminate duplication in our code.

This simple rule forces us how to think about our design and how to achieve low coupling and high cohesion in our components.

This allows you as the software developer to imagine and design how you’d interface with your implementation based on the interaction with your API by providing commands to objects and expecting certain results from those commands given.

This link between what you are providing and what you are expecting is the assertion that your software actually works.

Following this technique of development allows you to reduce your fear of code writing that doesn’t work because you are only writing code to satisfy an automated test.

Introduction to the rhythm

Remember the rhythm I’m always talking about to writing tests in a Test-Driven way? Here it is in it’s entirety and it is as follows:

  1. Quickly add a test.
  2. Make it compile.
  3. Run this test to check that it fails.
  4. Make it run.
  5. Make it right (refactor to clean code).

The common approach with TDD is to imagine our API for the clients use (what it will look like from the outside). Describing the API is like telling a story of the steps in an operation. But not all stories are beautiful, we’ll have to work our way backwards to write code that offer simplicity first to later write more complex code that becomes uglier and realistic in it’s design.

The blogging platform example

The source code for this example can be found on GitHub

Imagine a scenario where you’re implementing some functionality to authenticate a user who is trying to log into your software with a provided username and password. Second, specific methods (that we define) are restricted to require correct authorization and cannot be called without first logging in.

In the context of a blogging platform software, authorization is common in the following use case: A user has to be an owner of blog post to enable modification, removal or publication of their post. No other user (except an admin or security roles who are permitted) can modify, remove or publish their blog post. Authentication and authorization patterns like these are common throughout many software systems, were we want to enforce ownership and privacy of data.

What kind of design will we need to authenticate a user and programmatically allow some actions (e.g. publishing a new post) to require our user to be first authenticated (check the identity) and authorized (has permission to) to proceed with the action?

We need to have the ability to authenticate a user based on their credentials. This is the same process of logging into software. This includes an operation of validating the user’s username and password. The next part of course is to store that user in memory that’s easily queryable for use on authorization (before allowing execution on certain methods) in our software. This pattern is commonly know as a user session.

To keep what we just discussed in mind it’s a good practice to maintain a little to-do list of what we need to do and to remind ourselves of the tasks that need to be done. We will add to this list along the way as we encounter more tests that we want to add. We’ll start with the first item as that seems to be the most interesting.

  1. Authenticate user with username and password.
  2. User lookup.
  3. Queryable user session.
  4. Include pre-authorization check for methods that require authentication.

Starter test

So what test do we need to add first to get the ball rolling? In other words what should our first test look like? How can we model it in such a way that maintains simplicity and does not jump straight to realism? Let’s just give it a bash:

@Test
public void testUserLoginWithCorrectCredentials() throws Exception {

    // Given that this current user exists
    String username = "adrianvdh";
    String password = "hello123";
    AuthenticationService authenticationService = new AuthenticationService();

    // When authenticating
    authenticationService.login(username, password);

    // Then the current user must be valid
    Assert.assertEquals("adrianvdh", authenticationService.currentUser.username);
    Assert.assertEquals("hello123", authenticationService.currentUser.password);
}

Avoid realistic design

If we wrote our test with the intent of being realistic about our design we would slow down the feedback loop to minutes rather than ideally seconds. Realistic design given in a test is less focused on our intent which introduces a distraction from our aim, which is working functionality. Ideally we would have a Session object that AuthenticationService injects a User into. But we’ll only introduce that nicer design later in our Refactor to clean code step. At the moment we can ignore our temptations and get the same functionality that our test is asking for through a naive design not the ideal one. This act of distraction is true because I initially tried describing Session as a singleton object. Then in the proceeding step (Make it run) implementation Session object’s functionality required much more effort. Avoid this like you would flews, loneliness and debt.

If you ran this code, it wouldn’t compile. The next step it to get it to compile.

There are quite a few compilation errors to fix. We have 6 compilation errors by the way.

  • No class AuthenticationService.
  • No method login(String, String) for our AuthenticationService class.
  • No instance field currentUser for our AuthenticationService class.
  • No User class with fields username and password.

Rats! That looks like a lot of work for such a little test; in that case the key is to have positive attitude towards what we do. Let’s get a move on!

We’ll create our class AuthenticationService first, like this:

public class AuthenticationService {
}

Then implement the login(String, String) method:

public class UserService {
    public void login(String username, String password) {

    }
}

We also need our currentUser instance field for our AuthenticationService class:

public class AuthenticationService {
    public User currentUser;

    public void login(String username, String password) {

    }
}

And finally the User class itself:

public class User {
    String username;
    String password;
}

Now our test runs but fails. But this is progress so far. We expected two results in our test, the first expected value of “adrianvdh” got an actual null as a result.

The next step to solve this problem is to use the Fake It approach.

Fake it

The quickest solution to get the test to pass is to use the Fake It approach. You simply return constant values to get your test to pass, and then gradually swap out the constants for expressions that return the same value.

When the login(String, String) method is invoked we’ll instantiate our currentUser field with a new User instance which has values hard-coded already.

public class AuthenticationService {
    public User currentUser;

    public void login(String username, String password) {
        currentUser = new User();
    }
}

Then assign hard-coded values onto our User class’ properties:

public class User {
    String username = "adrianvdh";
    String password = "hello123";
}

Be hold. Our test passes!

What’s the point of our first test? It’s there but doesn’t add any value as we are just testing hard-coded/fake values. So what gives? The point of the initial test with hard-coded values is to act as a test net just in case we loose our grip on the wall of code and fall down and have something to land onto.

Often the first approach to getting your test bar to go green is to fake it. Our net will support us for any future changes that we make to our code as we (to quote Kent Beck) “gradually transform our constants into expressions using variables”.

But we are not done yet! We are missing the last step from the rhythm of TDD, refactor to remove duplication (make it right). In our case we have duplication in our User class and in our test data.

There isn’t a single step to remove duplication. We are going to be brave and stretch our little leg just a bit further to be courageous:

public class AuthenticationService {
    public User currentUser;

    public void login(String username, String password) {
        User foundUser = new User();
        foundUser.username = username;
        foundUser.password = password;

        if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
            currentUser = foundUser;
        }
    }
}

And in another step we need to remove that duplication from the User class too:

public class User {
    String username;
    String password;
}

I don’t like the current state of our code. The test will always pass and never fail, but there is good reason too. We need to mock out our queried (found) user with credentials matching the arguments passed in through our method. This found user will be replaced in the next step with a lookup strategy, but that isn’t what matters at the moment. Our authentication logic is what really matters. It’s what asks the question of, do the credentials that are passed in actually belong to anyone in the system? If so create a session for them.

We can finally knock off our first item on our todo list and highlight the next item: 1. Authenticate user with username and password. 2. User lookup. 3. Queryable user session. 4. Include pre-authorization check for methods that require authentication.

User lookup

The last section went by really slowly. Do we have to always take such small steps when practising TDD? No of course not! The practice is about being able to take small incremental steps. That doesn’t necessarily mean that you always should, but use it as a tool for when things are a bit hairy and you need a magnifying glass and a pair of tweezers to do your job 😉

Our AuthenticationService class has a fake implementation thus far. There is no way to lookup a user based on their username and at the moment we’re kind of reliant on a mock user. The better solution in my mind is quite simple:

public class AuthenticationService {
    public User currentUser;

    List<User> users = new ArrayList<>();
    {
        User user = new User();
        user.username = "adrianvdh";
        user.password = "hello123";
        users.add(user);
    }

    public void login(String username, String password) {
        User foundUser = users.get(0);

        if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
            currentUser = foundUser;
        }
    }
}

That should suffice for the previous implementation and our test still passes! We just needed to fake it for the sake of getting the green on our test. But we still need to add the ability to find a user by their username.

AuthenticationService

public void login(String username, String password) {
    User foundUser = null;
    for (User userInCollection : users) {
        if (userInCollection.username.equals(username)) {
            foundUser = userInCollection;
            break;
        }
    }

    ...
}

There. Now we are querying the user based on his username. The test still passes and all is well (for the most part). But what if the user could not be found? Then what should we do? Will we still interact with the AuthenticationService the same way? Our guess about designing the interface of AuthenticationService is no more perfect than our guess about the behaviour of our implementation.

Well If we change the way we describe our interaction with our interface (provide invalid user credentials) then by default we have to change the way our implementation will handle that interaction by throwing an exception.

Currently now the behavior of our login() method will do nothing if the credentials don’t match anybody. We need to alert the user that something went wrong.

Triangulation

In Test-Driven-Development there is this cool thing called Triangulation (which I’ll get to in a moment). First it is important to adhere this common rule that we should only write code to get a test to pass because the way we interface with our system is all described in our test, it wouldn’t make sense to write any other code that isn’t covered by the test. Getting back to Triangulation, we want to translate our design side effect (yes, it’s a design side effect) into another test. So in order to test a case where the user does not exist, we need to write another test that covers this case:

@Test(expected = UserNotFoundException.class)
public void testUserLoginWhereUserDoesNotExit() {

    // Given that this current user doesn't exist
    String username = "john69";
    String password = "hello123";
    AuthenticationService authenticationService = new AuthenticationService();

    //When authenticating
    authenticationService.login(username, password);
}

There’s only one problem. Our new UserNotFoundException class doesn’t exist yet. So we’ll have to create it:

public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

Run our tests and… It fails! Expected exception: c.s.t.UserNotFoundException

Since we have a new test that doesn’t pass it looks like we need to write some code to make it pass! This is the common rule that we described earlier.

public void login(String username, String password) {
    User foundUser = null;
    for (User userInCollection : users) {
        if (userInCollection.username.equals(username)) {
            foundUser = userInCollection;
            break;
        }
    }

    if(foundUser==null) {
        throw new UserNotFoundException(String.format("User with username %s could not be found!", username));
    }

    if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
        currentUser = foundUser;
    }
}

Run both our tests and they both pass! Hooray!

But I just noticed two things that I don’t like. First, what happens when the authentication process fails? i.e. The user’s password is incorrect. How would we know that the provided password was incorrect? Currently we don’t handle that case. Second why is the users collections hardcoded in our UserService class?

These are important side affects we want to eventually address. So we can update our test list accordingly. We can cross off the User lookup step since we have that functionality implemented already. Then we’ll add two new tasks for refactoring.

  1. Authenticate user with username and password.
  2. User lookup.
  3. Queryable user session.
  4. Include pre-authorization check for methods that require authorization.
  5. Authentication failure side effects? NEW
  6. Abstract user lookup to a user repository NEW

Sometimes you’ll feel the urgency to clean up your mess before you can continue working, so we’ll do that now. Plus we are in the phase of our TDD cycle where it demands refactoring to our code.

Remember our goal of clean code. Getting the code to work is our first priority, getting it to clean code is the next priority.

The concept of Triangulation is important because the more that you do it the more it will gradually describe the overall behaviour about the system. Once you’re happy with how the overall behaviour works then can you refactor to clean code. If on the other hand your ability to think about the design for a certain behaviour isn’t that clear you can use triangulation to think about the problem from a different angle. Perhaps the design will become clearer then.

Initially we got our tests to work by providing fake implementations and then gradually moved into real implementations by being cautious and taking teeny-tiny steps moving our hard-coded user into a collection then just returning the first item until we could find a user by their username.

Theere are three known strategies for getting our tests to pass.

  1. The fake it (until your make it). Just return a constant and until you’ll gradually replace constants with the real code.
  2. The obvious implementation. Just use the real implementation immediately if and only if you don’t have to think hard about the design.

It’s quite common to shift between these two variations. Some design is damn straight obvious and you can assure yourself with tests that what you are describing is still obvious to the computer. Other times you’ll have to go slow and iron it out until you have a real implementation that works.

The third style of course is triangulation which was demonstrated above just a few moments ago.

Translating the feeling of our anxiousness into another test is a common theme in TDD. You’ll know when the time is right to write another test when you have the feeling that your single test does not cover all the angles of your current implementation’s behaviour.

Abstract user lookup to a user repository

Having concluded triangulation we can continue with our work of abstracting away our user lookup strategy to some sort of user repository. Currently our login() method has too many responsibilities to be easily testable. To make it easily testable we need to improve our design. So we’ll refactor.

How are we going to approach this refactoring? It seems to be quite a big leap. The way I have learned to tackle tests that cover large leaps in design is to not take such large leaps. We want to be able to maintain small steps at a granular level. To think like this you need to trust your instinct to allow yourself to think about how to write tests at such small steps. Let’s look at the following as an example of trusting our instinct:

@Test
public void testFindByUsername_UserIsFound() {
    String username = "adrianvdh";
    UserRepository userRepository = new InMemoryUserRepository();

    User user = userRepository.findByUsername(username);

    Assert.assertEquals("adrianvdh", user.username);
}

The result of this is that we won’t be coupled to functionality that first has to try find a user and only then authenticate him. Remember that methods and classes should only have a single responsibility. You will notice (if you have not already) that having multiple responsibilities inhibits us from testing our functionality at a very granular level in isolation. We simply cannot triangulate enough to be able to test something that has one to many responsibilities.

Remember the good-ol’ principles of software engineering? Our use of SOLID principles specifically the Dependency Inversion Principle will be defined next.

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend upon details. Details should depend upon abstractions.

(This principle was invented by Robert C. Martin and discussed in his paper The Dependency Inversion Principle (1996))

Do you notice the use of an interface called UserRepository and our concrete InMemoryUserRepository implementing that interface? Is it a little obsessive? At the moment yes! Does it add much value to our design at the current moment? No. The point I’m trying to make with this refactoring is teach you how to write code that is clean and scalable, so further down the road when we need our UserRepository to be hooked up to a relational database, we can do so with ease because we have depended on an abstraction rather than an implementation, as the DIP states.

So the first step of course is to fix those compilation errors! We have three of them:

  • No UserRepository interface.
  • No InMemoryUserRepository class that implements our UserRepository.
  • No findByUsername(String) method on our UserRepository interface.

So let’s get down to it one little step at a … No wait! I think you are familar with small steps by now! This article is long enough! Let’s get down to it in a speedier manner!

public interface UserRepository {
    User findByUsername(String username);
}

(That automatically adds our findByUsername(String) method on our interface.)

Next step our InMemoryUserRepository!

public class InMemoryUserRepository implements UserRepository {
    @Override
    public User findByUsername(String username) {
        return new User();
    }
}

Okay, we made our test compile, yet our test still fails. So what can we do to get our test to become green? Well by copying the AuthenticationService‘s code directly and putting it into our new InMemoryUserRepository class. It should look something like this:

public class UserRepositoryImpl implements UserRepository {
    Set<User> users = new HashSet<>();

    {
        User user = new User();
        user.username = "adrianvdh";
        user.password = "hello123";
        users.add(user);
    }

    @Override
    public User findByUsername(String username) {
        User foundUser = null;
        for (User userInCollection : users) {
            if (userInCollection.username.equals(username)) {
                foundUser = userInCollection;
                break;
            }
        }

        return foundUser;
    }
}

This solves the make it run step in our TDD cycle, but just in a rudimentary way. But since this is just a refactor we can forgive ourselves for our sin of copying and pasting code.

So what if the user could not be found by their given username? We need a new test case for that right? Well say no more! We can write a little test for that too.

@Test
public void testFindByUsername_UsernameNotFound() {
    String username = "john69";
    UserRepository userRepository = new InMemoryUserRepository();

    User user = userRepository.findByUsername(username);

    Assert.assertNull(user);
}

Now if we run our test you’ll notice that is passes with a green flag immediately! Well since we just moved some functionality from our AuthenticationService class into our InMemoryUserRepository and to my knowledge we didn’t change any functionality, but the tests can speak for themselves.

Now I feel a lot better about myself that we have covered two of the common angles faced when performing a user lookup.

We can now get back to our AuthenticationService class. Our AuthenticationService can then depend on this new interface UserRepository which allows it not to care about our implementation details at all. In our case our UserRepositoryImpl (which is just an in-memory lookup) will be passed in as a dependency to our AuthenticationService class.

To get started with using the new UserRepository with our AuthenticationService class we need to pass a instance of InMemoryUserRepository to the constructor of our AuthenticationService class. We need to write a test to compose our new use of the AuthenticationService class along with the UserRepository. Luckily for us we already have a previous test that we can supe up to use our new repository class.

Before we begin, we should take note of the changes we are going to make in the next steps as we move on. This is what I speculate that will happen in the next coming minutes: (We’ll start with step 7)

  1. Authenticate user with username and password.
  2. User lookup.
  3. Queryable user session.
  4. Include pre-authorization check for methods that require authorization.
  5. Authentication failure side effects?
  6. Abstract user lookup to a user repository.
  7. Supe up our old testUserLoginWithCorrectCredentials() test. NEW
  8. Change AuthenticationService to use our new UserRepository as a dependency. NEW
  9. Fix compilation errors with the testUserLoginWhereUserDoesNotExit() test (errors caused by step 8). NEW

And here is our little change to our first test:

@Test
public void testUserLoginWithCorrectCredentials() throws Exception {

    // Given that this current user exists
    String username = "adrianvdh";
    String password = "hello123";
    UserRepository userRepository = new InMemoryUserRepository();
    AuthenticationService authenticationService = new AuthenticationService(userRepository);

    // When authenticating
    authenticationService.login(username, password);

    // Then the current user must be valid
    Assert.assertEquals("adrianvdh", authenticationService.currentUser.username);
    Assert.assertEquals("hello123", authenticationService.currentUser.password);
}

You may notice that this test doesn’t even compile because there is not constructor in our AuthenticationService class that accepts our UserRepository interface. What do we do when our test doesn’t compile? Make it compile! (The next step in our TDD cycle.)

public class AuthenticationService {
    ...
    public AuthenticationService(UserRepository userRepository) {
    }
    ...
}

So our AuthenticationService class now depends on our new UserRepository. But in the process of that change we have broken another test. Remember our old testUserLoginWhereUserDoesNotExit() test? So what can we do now? How should we handle an interruption in this case? Trust your instinct! Mine is telling me “yeah sure we should not address an interruption in place of our first test, but another one that is not compiling prevents us from proceeding anyway”. Always trust your instinct when practising TDD as it will always help you.

@Test(expected = UserNotFoundException.class)
public void testUserLoginWhereUserDoesNotExit() throws Exception {
    ...
    AuthenticationService authenticationService = new AuthenticationService(null);
    ...
}

That should do for now! We should get back to our first test as that is what matters the most at the current moment; well unless you have some other things to address. Well do you? Okay… Okay good. You don’t.

Our first test (userLoginWithCorrectCredentials()) still passes. Well this invites us to clean up our AuthenticationService class to change some structure and code on behalf of we don’t break any behavior that is set by our “test net”.

A few moments later and our AuthenticationService class now looks like this:

public class AuthenticationService {
    public User currentUser;

    private UserRepository userRepository;

    public AuthenticationService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void login(String username, String password) {
        User foundUser = userRepository.findByUsername(username);

        if(foundUser==null) {
            throw new UserNotFoundException(String.format("User with username %s could not be found!", username));
        }
        if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
            currentUser = foundUser;
        }
    }
}

Run our first test and it passes. Yay! Okay our next job is to get the second test to pass as like the first one. I think this little amendment to our test case should do the trick:

@Test(expected = UserNotFoundException.class)
public void testUserLoginWhereUserDoesNotExit() {

    // Given that this current user doesn't exist
    String username = "john69";
    String password = "hello123";
    UserRepository userRepository = new InMemoryUserRepository();
    AuthenticationService authenticationService = new AuthenticationService(userRepository);

    //When authenticating
    authenticationService.login(username, password);
}

Our test passes and we got the green we asked for. To keep the ball rolling we can quickly take a look at the authentication failure side affect item on your test list.

In my mind the test case is very similar to our previous test that we just wrote. Our previous test checks if the user is not found given the username throw an exception, quite simple. Our authentication side affect test should address the case where the password is incorrect for the given username. The same pattern should apply, check if the password is incorrect then throw an exception.

@Test(expected = AuthenticationFailureException.class)
public void testUserLoginWhereUserPasswordIsIncorrect() throws Exception {

    // Given that this current user has the incorrect password
    String username = "adrianvdh";
    String password = "flowers2006";
    UserRepository userRepository = new InMemoryUserRepository();
    AuthenticationService authenticationService = new AuthenticationService(userRepository);

    // When authenticating
    authenticationService.login(username, password);
}

Notice the subtle differences. We changed the expected exception to a new one that doesn’t exist yet. We want our AuthenticationService class to throw an exception if the password is incorrect. Our username is still valid, but the password is no longer any good. If you decided to run this code you’ll be introduced to just one compilation error:

  • No AuthenticationFailureException was found.

Let’s go ahead and create that exception class real quick:

public class AuthenticationFailureException extends RuntimeException {
    public AuthenticationFailureException(String message) {
        super(message);
    }
}

There we go, no more compilation errors! Run all tests and.. yes, as expected our remaining test is still failing. What should we do? Well definitely not delay and get our test to pass ASAP!

We can change the AuthenticationService to throw a AuthenticationFailureException in the case that our user’s password doesn’t match.

AuthenticationService

public void login(String username, String password) {
    User foundUser = userRepository.findByUsername(username);

    if(foundUser==null) {
        throw new UserNotFoundException(String.format("User with username %s could not be found!", username));
    }

    if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
        currentUser = foundUser;
    }
    else {
        throw new AuthenticationFailureException("Provided password was incorrect!");
    }
}

We can run all our tests and yes, they all are passing as expected. Well done!

It looks like we have finally met most of our tasks on our list. Let’s take a look at that old list and see how we have progressed:

  1. Authenticate user with username and password.
  2. User lookup.
  3. Queryable user session.
  4. Include pre-authorization check for methods that require authorization.
  5. Authentication failure side effects?
  6. Abstract user lookup to a user repository.
  7. Supe up our old userLoginWithCorrectCredentials() test.
  8. Change AuthenticationService to use our new UserRepository as a dependency.
  9. Fix compilation errors with the userLoginWhereUserIsNotFound() test (errors caused by step 9).

Woah! That’s quite a lot that we have done. We can finally move onto the second part of getting this damn authentication part of our model to work.

Before we move onto the next part, let’s quickly review that changes we have made in our refactoring.

  • Realized our AuthenticationService class had to many responsibilities.
  • Moved our user querying code to a new class InMemoryUserRepository.
  • Created new tests to support our new InMemoryUserRepository class.
  • Fixed AuthenticationService tests to use our new UserRepository as a dependency.
  • Fixed up some side affects of our AuthenticationService class we discovered earlier.

Queryable user session

Our previous to-do had become a bit cluttered up by previous reservations to problems in our life. To avoid stress of having clutter and opting for an minimalist lifestyle we’ll move our pending items onto a new list. (We’ll start with the first one as that seems to be the one who is crying presidency to make our application to work successfully – so we’ll highlight that one).

  1. Queryable user session.
  2. Include pre-authorization check for methods that require authorization.

We are ready to go ahead and start with creating a session object in which the User object can live once he has logged in. We can amend our testUserLoginWithCorrectCredentials() test to use our new Session singleton.

@Test
public void testUserLoginWithCorrectCredentials() throws Exception {

    // Given that this current user exists
    String username = "adrianvdh";
    String password = "hello123";
    UserRepository userRepository = new InMemoryUserRepository();
    AuthenticationService authenticationService = new AuthenticationService(userRepository);

    // When authenticating
    authenticationService.login(username, password);

    // The the current user session must be valid
    Session userSession = Session.getCurrentSession();
    Assert.assertEquals("adrianvdh", userSession.getAuthenticatedUser().username);
    Assert.assertEquals("hello123", userSession.getAuthenticatedUser().password);
}

As you may have guessed we have those compilations errors back to bite us in the ass.

  • No class named Session.
  • No static method getCurrentSession()
  • No method getAuthenticatedUser()

Let’s give them a wack and make it run. But hold on! I’m sure by now we’re confident enough to use the obvious implementation approach. Our Session class is just a singleton anyways:

public class Session {
    private static Session instance = null;
    public User currentUser;

    private Session() { }

    public static Session getCurrentSession() {
        if(instance==null)
            instance = new Session();
        return instance;
    }

    public User getAuthenticatedUser() {
        return currentUser;
    }

    public void setAuthenticatedUser(User user) {
        this.currentUser = user;
    }
}

Run our tests and we run into a NullPointerException exception.The reason is that we have no implementation to satisfy our test, duh! It looks like we have to fix that. Let’s give it a shot:

AuthenticationService

public void login(String username, String password) {
    User foundUser = userRepository.findByUsername(username);

    if(foundUser==null) {
        throw new UserNotFoundException(String.format("User with username %s could not be found!", username));
    }

    if(foundUser.username.equals(username) && foundUser.password.equals(password)) {
        Session session = Session.getCurrentSession();
        session.setAuthenticatedUser(foundUser);
    }
    else {
        throw new AuthenticationFailureException("Provided password was incorrect!");
    }
}

But also don’t forget to remove our currentUser User field from our AuthenticationService as it now lives within the Session class.

We can finally run our tests and it passes! We are making good progress.

Let’s get back to our good-old check list. We can cross out our first item as it’s done and dusted and we can move onto our next test.

  1. Queryable user session.
  2. Include pre-authorization check for methods that require authorization.

We can also rename the second item on the list to something that is more specific, like this:

  1. Queryable user session.
  2. Publish post requires authorization.

Pre-authorization

You may be asking, what in the is authorization and how should it work?

Well in my mind authorization is a little check that you can put before methods to ensure the user trying to invoke that method has permission to do so.

Use cases for this procedure include privacy of data and method calls requiring permission to be invoked. For example I the author of a draft blog post only want to be able to publish my post using some sort of publishPost(Post) method, no other user can do so. Or perhaps we only allow the author of their draft blog post to read their unpublished content on the blogging platform.

How could we take the approach of implementing this? I don’t have such a clear design idea on how we should do this. We need to first define our business requirement and break it down into smaller cases that we can address easily.

  1. I want to publish a draft post using some sort of post service. We can write a test case for this that checks if our published post’s release status says it’s published.
  2. To prevent anyone from taking your post and publishing it, I want to have some security constraint set on the publish method that checks if the user who is currently logged in is the same user who is the author of that blog post.

Simple, there are two pieces to our little puzzle and we can amend our test list to split our our parent test into two tests. But we’ll keep the parent test on our list because we’ll need it later.

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Child test: Authorization before publication.
  4. Publish post requires authorization.

Test-Driven-Development has a principle for addressing situations like this. We can write a set of child tests that represents a step in our requirement (mentioned above). With consistency we can achieve our bigger goal of implementing the feature.

We can actually start with the first piece which the ability to publish a blog post.

First child test: Publish blog post

We’ll actually just start with the assertion first and work out way up to implementing the details that make the assertion pass. It is important to ask yourself what is the desired outcome of getting past the constraint plugged into our publishPost(Post) method? Well, I want to be have the post’s status changed to published. Among other things such as sharing the post to social media, we should keep it quite simple first (I constantly have to remind myself of this all the time – so that means you too) to prove that our code works and then only clean it up and improve the design later.

Remember, assert first and avoid realistic design.

@Test
public void publishPost() throws Exception {
    ...
    Assert.assertTrue(post.published);
}

I’m not to concerned in the beginning about the details in place of the ellipses. I’m only concerned about my assertion and the test method name describing that assertion.

Where does the does this post come from? For now it can come from the thing that publishes it:

@Test
public void publishPost() throws Exception {
    ...
    Post post = postService.post;
    Assert.assertTrue(post.published);
}

And how is the post published? It looks as if I named the thing that publishes our post PostService. So we’ll stick with that:

@Test
public void publishPost() throws Exception {
    ...
    PostService postService = new PostService();
    postService.publish(newPost);
    Post publishedPost = postRepository.findMostLastPublishedPost();
    Assert.assertTrue(publishedPost.published);
}

How is our newPost instantiated? With a constructor that provides a title, a body and the author’s username.

@Test
public void publishPost() throws Exception {
    Post newPost = new Post("Hello world!", "This is my first post!", "adrianvdh");
    PostService postService = new PostService();
    postService.publish(newPost);
    Post post = postService.post;
    Assert.assertTrue(post.published);
}

It’s clearly set out. We have followed that base principles of Test-Driven-Development. Remember them. Assert first, avoid realistic design and get our test to pass as fast as possible. There are a few more principles, but you’ll learn them along the way 😉

If you run this test, we will run into quite a few compilation errors. Let’s fix them as quick as possible:

  • No Post class with our three-arg constructor.
  • No public field published on our Post class.
  • No PostService class with a fancy method publish(Post).
  • No public field post on our PostService class.

Let’s create our Post class:

public class Post {

    public boolean published;

    public Post(String title, String body, String authorUsername) {

    }
}

and our PostService class with our publish method:

public class PostService {
    public void publish(Post post) {

    }
}

Wait! I almost forgot the public field post in our service class:

public class PostService {
    public Post post
    ...
}

Last but not least, our Post class, with our little published boolean set to false:

public class Post {
    public boolean published = false;

    public Post(String title, String body) {
    }
}

Right, let’s run our test and see what happens.

Oh boy. Looks like we have NullPointerException floating around some where.

Let’s try the Obvious Implementation approach to get our test to pass.

public class PostService {
    public Post post;

    public void publish(Post post) {
        post.published = true;
        this.post = post;
    }
}

That seems obvious enough don’t you think? Our design is simple and quite terrible at the same time. As a reminder we have completed four out of five steps of the TDD cycle:

  1. Quickly add a test.
  2. Make it compile.
  3. Run this test to check that it fails.
  4. Make it run.
  5. Make it right (refactor to clean code).

But the last step remains. I can’t persuade myself to move onto the next section without completing this cycle. We first have to make it right before moving on!

@Test
public void publishPost() throws Exception {
    PostService postService = new PostService();
    Post newPost = new Post("Hello world!", "This is my first post!", "adrianvdh");

    postService.publish(newPost);

    Post post = postService.findLatestPublishedPost();
    Assert.assertTrue(post.getReleaseStatus().equals(ReleaseStatus.PUBLISHED));
}

The first step is to create a new method findLatestPublishedPost() on our PostService class.

public class PostService {
    ...
    public Post findLatestPublishedPost() {
        return post;
    }
}

We’ll also have to create our ReleaseStatus enum like so:

public enum ReleaseStatus {
    PUBLISHED
}

Then we’ll have to modify our Post class to include our ReleaseStatus field as well:

public class Post {

    public ReleaseStatus releaseStatus;

    public Post(String title, String body, String authorUsername) {

    }

    public ReleaseStatus getReleaseStatus() {
        return releaseStatus;
    }
}

Run out test and …ooh! Looks like we forgot something on our publish(Post) method…

public class PostService {
    public Post post;

    public void publish(Post post) {
        post.releaseStatus = ReleaseStatus.PUBLISHED;
        this.post = post;
    }

    public Post findLatestPublishedPost() {
        return post;
    }
}

The refactor is going great so far being that our test still runs and passes, but we forgot one little thing… We need a post repository in there; remember the whole spew about abstracting our data to a repository earlier? Well we need to do that again now:

@Test
public void testSaveAndReturnLatestPublishedPost() throws Exception {
    PostRepository postRepository = new InMemoryPostRepository();
    Post newPost = new Post("Hello world!", "This is my first post!", "adrianvdh");
    newPost.releaseStatus = ReleaseStatus.PUBLISHED;

    postRepository.save(newPost);

    Post savedPost = postRepository.findLatestPublishedPost();
    Assert.assertThat(savedPost, Matchers.is(newPost));
}

One thing you’ll notice is that our PostRepository interface and InMemoryPostRepository class doesn’t exist, so we’ll have to work our make and create them.

Here’s what our PostRepository should look like:

public interface PostRepository {
    void save(Post post);

    Post findLatestPublishedPost();
}

And then our InMemoryPostRepository as well:

public class InMemoryPostRepository implements PostRepository {

    private Post post;

    @Override
    public void save(Post post) {
        this.post = post;    
    }

    @Override
    public Post findLatestPublishedPost() {
        return post;
    }
}

Few! I feel better and I hope you do too. Let’s run our latest test and see what happens.

Yip it passes! What a relief. Let’s go back to our publish post test where we can make a refactoring to include our new PostRepository interface in our PostService to take care of the ugliness of our PostService has to endure.

Let’s demonstrate that refactoring:

@Test
public void publishPost() throws Exception {
    PostRepository postRepository = new InMemoryPostRepository();
    PostService postService = new PostService(postRepository);
    Post newPost = new Post("Hello world!", "This is my first post!", "adrianvdh");

    postService.publish(newPost);

    Post post = postRepository.findLatestPublishedPost();
    Assert.assertTrue(post.getReleaseStatus().equals(ReleaseStatus.PUBLISHED));
}

It’s so much better right? But there is still one more change that we have to make right.

Let’s add our PostRepository into the constructor of our PostService class to make our post being published save to our post repository implementation.

public class PostService {
    public PostRepository postRepository;

    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    public void publish(Post post) {
        post.releaseStatus = ReleaseStatus.PUBLISHED;
        postRepository.save(post);
    }
}

We can run our publish post test one last time to see that it works.

Does it work? Run it! Go on… Yes it works.

The one thing that’s at the back of my head that’s bugging me is our InMemoryPostRepository class. It’s kind of crappy; but because we allow it to be for demonstrative purposes it serves it due diligence well enough that we can excuse it’s crappyness.

Second child test

We’re finally at the next part of our authorization section which is to write the piece that checks if the current user is the author; also know as the authorization check.

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Child test: Authorization before publication.
  4. Publish post requires authorization.

So how should this authorization piece actually work? What will happen if the user is authenticated (logged in) and second, not authorized to proceed with an action? Well nothing at all, everything will be fully operational as if this mechanism weren’t there. But in the case that we (1) aren’t authenticated and (2) authorized we’ll have the bar’s bouncer kick us out immediately!

We can transform our list to include two decisive tests and remove the other very vague test on our test list.

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Method invocation requires authentication.
  4. Method invocation requires owner authorization.
  5. Publish post requires authorization.

Starting with the first test on our test list, how are we going to describe our test? We need to think in the case of publishing our post. At the start of our publish method we can have some logic that first check’s if the user’s session exists and later if the user is the author of the blog. If neither of these cases are true we should throw some sort of exception.

Now that we have grounded the functionality of our method, let’s talk about the assertion of our test.

The best way to describe the assertion is to check what will happen if the user isn’t authenticated and authorized. We can’t test the inverse case because there’s nothing to test other than the desired outcome of our method, which is a published blog post; but we haven’t gotten here yet. We need to test our authentication and authorization (security) piece in isolation before we can fit the publish post method and our security mechanism together.

Assertion first.

In JUnit, the expected attribute on our @Test annotation is our assertion.

@Test(expected = AuthenticationRequiredException.class)
public void methodRequiresAuthentication() throws Exception {

}

Now there are some compilation errors as expected:

  • No AuthenticationRequiredException exception class.

Go head and create it like this one.

public class AuthenticationRequiredException extends RuntimeException {
    public AuthenticationRequiredException(String message) {
        super(message);
    }
}

Now we need a when/action clause that contains the logic of checking the user’s session. In our test method we can add this guy as our when clause:

@Test(expected = AuthenticationRequiredException.class)
public void methodRequiresAuthentication() throws Exception {

    userAuthorization.requiresAuthentication();
}

But where does it come from? What will we need to provide it on creation? Nothing at the moment. It’s just going to inspect our Session object, so no constructor arguments needed.

@Test(expected = AuthenticationRequiredException.class)
public void methodRequiresAuthentication() throws Exception {
    UserAuthorization userAuthorization = new UserAuthorization();

    userAuthorization.requiresAuthentication();
}

There are a few more compilation errors. We don’t have a UserAuthorization class and no method requiresAuthentication() at all.

Oh crap! It has to be in this format:

  • No UserAuthorization class.
  • No requiresAuthentication() method in our UserAuthorization class.

Few! I feel a whole lot better.

What kind of logic should go in our requiresAuthentication() method? If the session for the current user doesn’t exist (or if there’s no session) then throw that runtime exception we created just a few moments ago.

Yeah, I like it! Let’s see what happens with the obvious implementation approach.

public class UserAuthorization {

    public void requiresAuthentication() {
        User currentUser = Session.getCurrentSession().getAuthenticatedUser();
        if(currentUser==null) {
            throw new AuthenticationRequiredException("This method requires a user to be logged in first!");
        }
    }
}

Okay, run it to see that it works.

Does it work?

Yes it does and we can cross that test case off of our test list:

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Method invocation requires authentication.
  4. Method invocation requires owner authorization.
  5. Publish post requires authorization.

So next up is our authorization test case. But what is special about this check? The special thing about this check is that it’s quite specific. We are wanting to validate if the current logged in user matches another username (the post’s author). So we’ll need to pass in author’s username as a parameter.

We can start off with a test that requires a new exception to be expected:

@Test(expected = AuthorizationDeniedAccessException.class)
public void userIsOwnerAuthorization() throws Exception {

}

So we’ll need to create this new runtime exception class like so:

public class AuthorizationDeniedAccessException extends RuntimeException {
    public AuthorizationDeniedAccessException(String message) {
        super(message);
    }
}

And then in our test method we can provide the when clause which passes in our post’s author’s username.

@Test(expected = AuthorizationDeniedAccessException.class)
public void userIsOwnerAuthorization() throws Exception {
    UserAuthorization userAuthorization = new UserAuthorization();

    userAuthorization.userIsOwner(ownerUsername);
}

We can try run our test, but it’s sure to fail. We’ll then have to create our userIsOwner(String) method in our UserAuthorization class.

We can go head and just the the Obvious Implementation technique here again.

UserAuthorization

 public void userIsOwner(String ownerUsername) {
    requiresAuthentication();

    if(!ownerUsername.equals(currentUser.username)) {
        throw new AuthorizationFailedException("The current user is not the owner!");
    }
}

We have also included the requiresAuthentication() method in the start because it’s required that the user be authenticated before he’s authored.

Let’s run our tests and see what happens. Both of them should be passing with flying green colours.

Let’s take a look at our test list. We’re done with the second test of our security mechanism, so we can cross it out.

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Method invocation requires authentication.
  4. Method invocation requires owner authorization.
  5. Publish post requires authorization.

I do want to quickly show you a newer version of the userIsOwner() method because I do think what I have built here is quite cool.

When we decide to use both of blogging platform’s publish(Post) method and our authorization mechanism together, we’ll need to pass in the post’s author’s username into the security mechanism. Why couldn’t we just pass in the post as a common entity that’s married to some sort of interface?

If what I said didn’t really make sense, then take a look here:

@Test(expected = AuthorizationFailedException.class)
public void userIsOwnerAuthorization_usingOwnerAwareEntity() throws Exception {
    UserRepository userRepository = new InMemoryUserRepository();
    UserService userService = new UserService(userRepository);
    userService.login("adrianvdh", "hello123");

    UserAuthorization userAuthorization = new UserAuthorization();
    OwnerAwareEntity ownerAwareEntityPost = new Post("John's post", "Hey, howzit!", "john69");

    userAuthorization.userIsOwner(ownerAwareEntityPost);
}

Isn’t that cool?

But to get it to compile we need to create our OwnerAwareEntity interface that looks like this:

public interface OwnerAwareEntity {
    String getOwnerUsername();

    void setOwnerUsername(String username);
}

Do you see where this is going?

Next our Post class has to implement that interface:

public class Post implements OwnerAwareEntity {

    public ReleaseStatus releaseStatus;
    private String authorName;

    public Post(String title, String body, String authorUsername) {
        this.authorName = authorUsername;
    }

    public ReleaseStatus getReleaseStatus() {
        return releaseStatus;
    }

    @Override
    public String getOwnerUsername() {
        return authorName;
    }

    @Override
    public void setOwnerUsername(String username) {
        this.authorName = username;
    }
}

And we need one last method on our UserAuthorization class that accepts an instance of our OwnerAwareEntity:

UserAuthorization

public void userIsOwner(OwnerAwareEntity ownerAwareEntityEntity) {
    requiresAuthentication();

    if(!ownerAwareEntityEntity.getOwnerUsername().equals(currentUser.username)) {
        throw new AuthorizationFailedException("The current user is not the owner!");
    }
}

Pretty nifty hey?!

Run that test and watch it pass with a green light!

Okay so next up is part where we bring the blog posting service and our security mechanism together.

We need a test case that describes the use of our authentication, then our when clause of publishing a new post and finally checking that the post has been published.

We’ll can copy our first test case which is to test our publishPost() method and give it a relevant test case name. Except that we remove the our assertion and replace it with a expected exception:

@Test(expected = AuthorizationDeniedAccessException.class)
public void publishPostThatRequiresAuthorization() throws Exception {

    PostRepository postRepository = new InMemoryPostRepository();
    PostService postService = new PostService(postRepository);
    Post newPost = new Post("John's post", "Hey, howzit!", "adrianvdh");

    postService.publish(newPost);

    Post post = postRepository.findLatestPublishedPost();
    Assert.assertTrue(post.getReleaseStatus().equals(ReleaseStatus.PUBLISHED));
}

This will fail.

But we can plug-in our authentication check in our publishPost(Post) method in our PostService class.

Don’t forget to instantiate the UserAuthorization class.

public class PostService {
    public PostRepository postRepository;

    private UserAuthorization userAuthorization;

    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
        this.userAuthorization = new UserAuthorization();
    }

    public void publish(Post post) {
        userAuthorization.userIsOwner(post);

        post.releaseStatus = ReleaseStatus.PUBLISHED;
        postRepository.save(post);
    }
}

It finally works! Give me a high five.

Now that we are done we can proudly cross off our last item on our test list and call our job here done!

  1. Queryable user session.
  2. Child test: Publish blog post.
  3. Method invocation requires authentication.
  4. Method invocation requires owner authorization.
  5. Publish post requires authorization.

The source code demonstrated in this post can be found on GitHub

Adrian van den Houten

I'm Adrian van Houten, founder of ScholarCoder and a passionate software developer for full-stack web development. Read more about me here.