How Functional Style Helps with Mocking Dependencies

functional programmingmockingtdd

From the interview with Kent Beck in a recent Java Magazine issue:

Binstock: Let’s discuss microservices. It seems to me that test-first on microservices would become complicated in the sense that some services, in order to function, will need the presence of a whole bunch of other services. Do you agree?

Beck: It seems like the same set of trade-ofs about having one big class or lots of little classes.

Binstock: Right, except I guess, here you have to use an awful lot of mocks in order to be able to set up a system by which you can test a given service.

Beck: I disagree. If it is in an imperative style, you do have
to use a lot of mocks. In a functional style where external dependencies are collected together high up in the call chain, then I don’t think that’s necessary. I think you can get a lot of coverage out of unit tests.

What does he mean? How can functional style liberate you from mocking external dependencies?

Best Answer

A pure function is one that:

  1. Will always give the same result given the same arguments
  2. Doesn't have any observable side effects (e.g. state changes)

Suppose we are writing some code to handle user login, where we want to check that the supplied username and password are correct and prevent the user logging in if there are too many failed attempts. In an imperative style our code might look like this:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    if (user == null)
    {
        return false;
    }
    if (user.FailedAttempts > 3)
    {
        return false;
    }
    // Password hashing omitted for brevity
    if (user.Password != password)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return true;
}

Its pretty clear that this is not a pure function:

  1. This function won't always give the same result for a given username and password combination as the result also depends on the user record stored in the database.
  2. The function can change the state of the database, i.e. it has side effects.

Also note that in order to unit test this function we need to mock out two database calls, FindUser and RecordFailedLoginAttempt.

If we were to refactor this code into a more functional style we might end up with something a bit like this:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    var result = UserLoginPure(user, password);
    if (result == Result.FailedAttempt)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return result == Result.Success;
}

Result UserLoginPure(User user, string pasword)
{
    if (user == null)
    {
        return Result.UserNotFound;
    }
    if (user.FailedAttempts > 3)
    {
        return Result.LoginAttemptsExceeded;
    }
    if (user.Password != password)
    {
        return Result.FailedAttempt;        
    }
    return Result.Success;
}

Note that although the UserLogin function is still not pure, the UserLoginPure function is now a pure function and as a result the core user authentication logic can be unit tested without needing to mock any external dependencies. This is because interaction with the database is handled higher up the call stack.