Delegating test components

It is more advisable to use delegation rather than extension.

Mocking and verification logic that depends on the used components is delegated to separate test objects. The delegates thus encapsulate and manage this logic individually.

The following code snippet shows the test case using components that define the car manufacture and car factory dependencies:

public class ManufactureCarTest {

    private CarManufacturerComponent carManufacturer;
    private CarFactoryComponent carFactory;

    @Before
    public void setUp() {
        carFactory = new CarFactoryComponent();
        carManufacturer = new CarManufacturerComponent(carFactory);
    }

    @Test
    public void test() {
        Specification spec = ...
        Car expected = ...

        assertThat(carManufacturer.manufactureCar(spec)).isEqualTo(expected);

        carManufacturer.verifyManufacture(expected);
        carFactory.verifyCarCreation(spec);
    }
}

The Component test dependencies specify the declared dependencies and mocks including the setup and verification behavior for our test cases. The idea is to define components that are reusable within multiple component tests, wiring up similar logic.

The following code snippet shows the definition of CarManufacturerComponent:

public class CarManufacturerComponent extends CarManufacturer {

    public CarManufacturerComponent(CarFactoryComponent carFactoryComponent) {
        entityManager = mock(EntityManager.class);
        carFactory = carFactoryComponent;
    }

    public void verifyManufacture(Car car) {
        verify(entityManager).merge(car);
    }
}

The class resides in the same package as the CarManufacturer class, but under the test sources. It can subclass the boundary to add mocking and verification logic. In this example, it is dependent on the CarFactory component, that also provides additional test logic:

public class CarFactoryComponent extends CarFactory {

    public CarFactoryComponent() {
        automation = mock(Automation.class);
        assemblyLine = mock(AssemblyLine.class);
        when(automation.isAutomated()).thenReturn(true);
    }

    public void verifyCarCreation(Specification spec) {
        verify(assemblyLine).assemble(spec);
        verify(automation).isAutomated();
    }
}

These components serve as reusable test objects that wire up certain dependencies and configure mocking behavior, accordingly. They can be reused within multiple component tests and enhanced without affecting usages.

These examples aim to give an idea of what is possible in order to write maintainable tests. For components being reused, more refactoring approaches should be considered, for example, using a builder-pattern like configuration to satisfy different situations. The Maintaining test data and scenarios section in this chapter contains more about how to write maintainable test code.

The benefit of component tests is that they run as fast as unit tests and yet verify more complex integration logic. The complex logic is tackled by delegation and encapsulation, increasing maintainability. The code and overhead required to setup is limited.

It makes sense to verify coherent business logic using component tests. Use case invocations are tested on a business level with technical low-level aspects being mocked away.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.211.106