Dariusz Mydlarz

Dariusz Mydlarz

Software Craftsmanship, Project Management & Productivity

QR code

How Do I Make Mocking Lot Easier?

Do you find configuring mocks for your tests a bit painful? I was there for a very long time.
Now I found a solution that makes it a lot easier and I would like to share it with you.

December 18, Krakow, ICE Congress Center
December 18, Krakow, ICE Congress Center

Mocking is hard in OOP. Probably you should avoid it at all cost. Objects has a lot of dependencies. It’s hard to maintain them in the given block. One thing relies on another, which relies on another, and so on. This makes test hard to maintain & read. Let’s take an example written with Spock.

def "should save user id within image"() {
    given:
        Image image = Mock()
        def userId = 1L
        User user = Mock()
        user.id() >> userId
        user.created_at >> LocalDateTime.now()
        UserRepository userRepository = Mock()
        userRepository.find(_ as Long) >> user
        ImageService imageService = new ImageService(userRepository)

    when:    
        Image savedImage = imageService.save(image, userId)

    then:
        savedImage.userId() == userId
}

Is it readable? No. Is it easy to find out what actually is tested? No. Is it maintainable in the long run? No.

What happens when for instance imageService requires additional user data like username or avatar? Would it be easy to refactor number of tests to address this change?

How would you mock failures in UserRepository? Missing users, throwing exceptions, and so on? Would that be easy?

I would like to convince you to replace mocking objects with faking them. Just like this.

def "should save user id within image"() {
    given:
        User user = new User.Fake()
        ImageService imageService = new ImageService(
            new UserRepository.Fake(user)
        )

    when:    
        Image savedImage = imageService.save(
            new Image.Fake(), 
            user.id()
        )

    then:
        savedImage.userId() == user.Id()
}

Isn’t it more clear which class we’re actually testing? Take a look how all internal dependencies has been hidden out from you. Hidden because they are not important for verifying this particular behavior.

Instead of mocking the UserRepository and providing to it all the objects it needs, or mocking methods’ behavior, etc, we just simply replaced the concrete implementation with a different one. Now we’re sure objects relies on abstractions instead of specific objects.

By this methodology we can simply put any fake type we want. Let’s take an example of broken UserRepository that throws exceptions. We simply can do it like this:


def "should save user id within image"() {
    given:
        def userId = 1L
        ImageService imageService = new ImageService(
            new UserRepository.Throwing(
                new IllegalStateException("User not found")
            )
        )

    when:    
        Image savedImage = imageService.save(
            new Image.Fake(), 
            userId
        )

    then:
        IllegalArgumentException ex = thrown()
        ex.message = "Invalid user ID"
}

You simply provided another implementation of UserRepository. The one that fakes the specific behavior you want to verify.

You don’t have to couple all your specs to all the methods interface provides. You don’t have to cope with refactoring changes, or adding/removing new methods to interface. You don’t have to repeat yourself in all the specs this dependency has been used.

You just simply address required changes in the source code. Yes, you read right. Fakes should be kept along the source code. Just like below.


interface UserRepository {
    User findById(long id)

    @AllArgsConstructor
    class Fake implements UserRepository {
        private final User user;

        Fake() {
            this(new User.Fake());
        }

        User findById() {
            return user;
        }
    }

    @AllArgsConstructor
    class Throwing implements UserRepository {
        private final Exception exception;

        Fake() {
            this(new Exception("Failed to fetch data"));
        }

        User findById() {
            throw exception;
        }
    }
}

Having it written like this you gain a number of benefits:

  1. You always have some defaults fake implementation — for test where details are not necessary — just call new UserRepository.Fake().
  2. You don’t have to use any mock frameworks - just use regular interface implementations,
  3. Fakes work well with refactors - which is not always a true for regular mocks (especially having source code in Java and test code in Groovy).
  4. You change the behavior inside the source code - so you can be sure that specs which don’t rely on any internal data break.
  5. Last but not least: you give all the clients of your code an easy tool for testing for free. They will for sure thank you.

I found this technique in Yegor’s Bugayenko book and initially came skeptically to it. But after giving it a try in my last project, we found it super useful. At the beginning it may look odd to have implementation classes inside the interface definitions but it’s easy to get use to it after a while. And it pays off in a long term. Writing specs for our applications without a need to get into details of objects that are not the subject of the spec made it more easy to think and write the tests scenarios. I am sure I am gonna implement this technique in my following projects.

I highly recommend this approach if you haven’t tried it. It let you get out of mocking at all actually and gives you much better visibility and operability over things that happen during tests.