Developers’ Tests

On Tests and Tools

SUT (aka. System Under Test) – test target being tested

DOC (aka. Dependency of Components) – collaborators and dependencies when testing

Types of Developers’ Tests

  • Unit Test
  • Integration Test
  • End-to-End Test Every type of test has different scope. Basically, unit test is in class level, integration test is in module level and end-to-end test is in application level.

Other types of tests:

  • System Integration Test – test with external systems/applications, services etc
  • User Acceptation Test – final user verification test
  • Load Test – verify system performance under pressure / load

Should Developers Test Their Own Code?

Yes!

Unit Tests

what is a Unit Test?

It’s to make sure the class you are working on right now works correctly.

You should test your classes** in isolation**. When writing unit tests it’s important to test a single class and nothing more. Forget about databases, Spring configuration files, and external web services.

Concentrate on the logic of your class!

Interactions in Unit Tests

Types of collaboration with an SUT

collaboration-of-SUT

Test class has direct operations with SUT, but its collaborations have indirect operations on the SUT state.

We use direct inputs and indirect inputs (SUT receiveing some message) to set the SUT in a required state and to invoke its methods.

The direct and indirect outputs (SUT sending a message) of the SUT are expressions of the SUT’s behaviours; we use them to verify whether the SUT is working properly.

State Testing vs. Interaction Testing

  • State Testing - uses direct inputs and outputs for what the results of actions.
  • Interaction Testing - concentrates on how messages are passed between collaborators With State Testing, the SUT is a black box. Interaction testing looks inside the SUT and verifies its internal parts.

Writing Unit Testing

Unit tests with no collaborator

parameterized

public class Money {
    private final int amount;
    private final String currency;
    public Money(int amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public int getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }

    public boolean equals(Object anObject) {
        if (anObject instanceof Money) {
            Money money = (Money) anObject;
            return money.getCurrency().equals(getCurrency())
                    && getAmount() == money.getAmount();
        }
        return false;
    }
}
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class MoneyParameterizedTest {
    @Parameterized.Parameters(name = "{index}: money({0}, {1})")
    public static final Object[] data() {
        return new Object[] {
                new Object[] {10, "USD"},
                new Object[] {20, "EUR"}
        };
    }

    @Parameterized.Parameter(value = 0) // first data value (0) is default
    public int amount;

    @Parameterized.Parameter(value = 1) // value is the index in parameter tuple
    public String currency;

    @Test
    public void constructorShouldSetAmountAndCurrency() {
        Money money = new Money(amount, currency);
        assertEquals(amount, money.getAmount());
        assertEquals(currency, money.getCurrency());
    }
}

test fixture

A test fixture is a fixed state of a set of objects used as a baseline for running tests. The purpose of a test fixture is to ensure that there is a well known and fixed environment in which tests are run so that results are repeatable. Examples of fixtures:

  • Preparation of input data and setup/creation of fake or mock objects
  • Loading a database with a specific, known set of data
  • Copying a specific known set of files creating a test fixture will create a set of objects initialized to certain states.

JUnit supports test fixture by using:

@BeforeClass
public static void setUpClass() {}

@AfterClass
public static void tearDownClass() {}

@Before
public void setUp() {}

@After
public void tearDown() {}

Phases of a unit test

phase

explanation

arrange

creation of all objects (except for the SUT) that are necessary for test execution

creation of the object whose functionality will be tested, and setting it in some initial state

act

execution of SUT methods to be tested

assert

verification of the test results

TDD (Test-Driven Development)

when to write test?

  • Test Last (aka. Code First) development
  • Test First development

How TDD do?

  • write a test that fails (       )
  • make the code work (       )
  • eliminate redundancy (       )

tdd

Note: refractor can be done for classes to test or the test code.

TDD should be applied to every level: unit test, integration test, end-to-end test. tdd-on-different-levels

 test double (stub, test spy, mock)

  • Dummies and stubs are used to prepare the environment for testing (test-fixture setting). They are not used for verification.
  • Test spies and mocks are to verify the correctness of the communication between the SUT and DOCs.  Bothe can also participate in test-fixture setting. Fake For the sake of completeness, let us describe another type of test double: a fake. Fake works almost as good as the real collaborator, but is somehow simpler and/or weaker (which makes it not suitable for production use). It is also usually “cheaper” in use (i.e. faster or simpler to set up), which makes it suited to tests (which should run as fast as possible). A typical example is an in-memory database that is used instead of a full-blown database server. It can be used for some tests, as it serves SQL requests pretty well; however, you would not want to use it in a production environment. In tests, fake plays a similar role to dummy and stub: it is a part of the environment (test fixture), not an object of verification. Fakes are used in integration tests rather than in unit tests, so we will not be discussing them any further.
package messenger;

/**
 * Created by Richard on 24/05/15.
 */
public class Messenger {
    private TemplateEngine templateEngine;
    private MailServer mailServer;
    public Messenger(MailServer mailServer, TemplateEngine templateEngine) {
        this.mailServer = mailServer;
        this.templateEngine = templateEngine;
    }
    public void sendMessage(Client client, Template template) {
        String msgContent = templateEngine.prepareMessage(template, client);
        mailServer.send(client.getEmail(), msgContent);
    }
}

interface MailServer {
    void send(Object email, String msgContent);
}

interface Template {
}

interface TemplateEngine {
    String prepareMessage(Template template, Client client);
}
package messenger;

import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;

/**
 * Created by Richard on 24/05/15.
 */
public class MessengerTest {
    // SUT
    Messenger messenger;

    // dummy
    Client client;
    Template template;

    // stub
    TemplateEngine templateEngine;

    // spy
    MailServer mailServer;

    final String CLIENT_EMAIL = "[email protected]";
    final String MSG_CONTENT = "Dear John! You are fired.";

    @Before
    public void setUp() {
        // dummy
        template = mock(Template.class);

        // stubs
        client = mock(Client.class);
        when(client.getEmail()).thenReturn(CLIENT_EMAIL);

        templateEngine = mock(TemplateEngine.class);
        when(templateEngine.prepareMessage(template, client)).thenReturn(MSG_CONTENT);

        // spy
        mailServer = mock(MailServer.class);

        // SUT
        messenger = new Messenger(mailServer, templateEngine);

    }

    @Test
    public void test1() {
        messenger.sendMessage(client, template);

        // verify interaction
        verify(mailServer).send(CLIENT_EMAIL, MSG_CONTENT);
    }
}