Skip to main content

El Arte de la Guerra... del Testing: Dobles de tests

2024-05-21
Madrid, Spain
Codemotion Madrid 2024
Clean CodeTestingJava

On May 21st, 2024, I had the opportunity to give a talk at Codemotion about test doubles, and how they can help us improve the quality of our code. It was my first talk at an event, and I had a great time. This talk is a brief introduction to a more complete talk, which I will give at another event in the future.

Below I share the content I discussed in the talk.

Introduction

Good afternoon everyone, my name is Aitor Santana and I come from the sunny Gran Canaria. I'm passionate about video games, a hobby that has been with me since childhood. However, my professional career has taken a different but equally exciting path: web development. I currently work on the backend side with Java at Lean Mind. Although I don't develop video games, the problem-solving skills and creativity I've developed through gaming have been very useful in my career. I'm here to share my experience in the fascinating world of software testing.

Context

To start, I'd like to give you a bit of context.

When I learned to handle tests with reasonable fluency and started going deeper, I hit a stumbling block: test doubles. Understanding these structures can be a significant challenge, as there are several types that adapt to different use cases.

For me it was a complicated concept to understand, and many people I know went through the same problem, so I thought about asking our beloved chat-gpt.

This was the statistic it produced — we probably can't trust this data 100%, but it shows that for the vast majority these are somewhat complex concepts. That's why I'd like to help anyone starting out to better understand these tools. I think this is like the art of war: know your enemy well to know how to fight them.

Types of Tests

To support my explanation, I'll use a small diagram from the book "Clean Craftsmanship by Robert C. Martin", which represents the types of doubles as a hierarchical structure, similar to inheritance.

This was what finally made things click in my head, what made all the puzzle pieces fit together. What we see here is Meszaros' terminology, which first appeared in the book "xUnit Test Patterns: Refactoring Test Code" by Gerard Meszaros. I think it's important to know each type of double, because in test libraries they are generally blurry, and many times that's what leads us to confusion.

LoginDialog

To support and explain the different types of doubles, I'll use a small exercise.

java
public interface Authenticator {
  boolean authenticate(String username, String password);
 }

 public class LoginDialog {
    private final Authenticator authenticator;
    private boolean isOpen = false;

    public LoginDialog(Authenticator authenticator) {
        this.authenticator = authenticator;
    }

    public boolean submit(String username, String password) {
        if(isOpen) {
            close();
            return authenticator.authenticate(username, password);
        }

        return false;
    }

    // More code...
 }

We have this Authenticator interface and the LoginDialog class. That interface represents the contract that the collaborator we pass through the constructor must follow. The goal is to test the behavior of the Login dialog and use the different doubles to run our tests. At the end of the session attendees will have a repo with the boilerplate, along with the different code examples we see in the session.

Dummy

Let's start with the simplest type of double, the dummy. A dummy is a type of test double used when you need to pass an object to a component under test, but the behavior of the double isn't relevant to the test in question.

java
public class AuthenticatorDummy implements Authenticator {
    @Override
    public boolean authenticate(String username, String password) {
        return false;
    }
 }

@Test
void when_closed_login_is_canceled() {
    Authenticator authenticator = new AuthenticatorDummy();
    LoginDialog dialog = new LoginDialog(authenticator);

    dialog.show();
    dialog.close();

    assertFalse(dialog.isOpen());
}

We want to test that the modal closes when we click cancel, so a dummy fits perfectly. We create a dummy that implements the interface, so we can inject it into our Login — it can return anything, we don't care.

Whether the user is authenticated is not relevant to testing the modal's behavior.

Resources

I'd like to leave you with some resources:

You can watch the full talk on the Lean Mind channel, and the code examples in the repository.