useful testing patterns

you finished your last tasks and want to complete the story. the build pipeline is green, you deployed the application to a test environment, prepared a demo, finally you are doing a story review and then comes the question: "but did you test it?". many people don't like tests because of the complexity of the production code, writing mocks or simply don't see the need of tests.


this article isn't about "why you should test your code", it's about some approaches to simplify testing to improve readability and reduce complexity, so one is not afraid to write some tests because they are too too difficult or too big.


we are all matured enough to know that we neither test for the testing sake nor to improve the code coverage.


the test method pattern


if your method in production code is short, your test is probably short, too. you can fast determine what the code is supposed to do, what valid input is and what makes your application crash. but if your test can't be read easily because of some test data creation or mock data definition, it helps to read the method name. in my job, i have seen lots of different approaches naming the test method.

  • enumeration: test1, test2, test3

  • test prefix with method name: testTransform, testProcess, testSave

  • simple description: testWritingInDb, testDeletingFromDisk

giving your test method no name at all and using ongoing numbers because of the method name conflict won't help neither you nor your fellows. let some time pass and you can't remember what your intention of this test was. you will start losing time figuring out what the test was supposed to do or maybe start writing a duplicate test.


prefixing test to your method name to build the test method name isn't a good choice, too. With this weak pattern you are motivated to write exactly one test because of the pattern restriction or you start to fall back to the enumeration pattern and keep using ongoing numbers as a suffix.


using simple descriptions isn't that bad. one can guess the intention of the test, but there are some parameters missing, which method is tested, does this method has any input or trigger and what is the generated output.


there is one pattern called whenGivenThen or givenWhenThen pattern. It determines what has to be tested, what the input is and what the expected outcome is. The template can be described as followed: when{methodName}Given{inputOrCondition}Then{OutcomeOrConsequence} Here are some examples how to use this pattern.

  • whenTransformGivenFilledEntityThenReturnFilledDtoWithoutId

  • whenProcessGivenTwoListsOfStudentsThenReturnMergedListOfDistinctStudents

  • whenSaveGivenNullNameThenThrowDbException


this pattern can help to prove readability of the test. you and your team know this pattern and can easily see what scenario of the test is. you can also switch the prefix, infix and the suffix word of the pattern, if you find other expressions more fitting, for example test{0}With{1}Result or test{0}Input{1}Output. the main point is that you and your team find a model and stick with it. it's way easier to read the same pattern over and over than trying to encrypt kind of random method names.


but this pattern isn't a solution for every problem. as test complexity rises and more conditions appear, the method name will become longer and longer. this solution has also its limits.


highlighting of tested instance


if your test has some mocks, test data and assertions, the chance is there that you lose track of your actual testing instance between the coding lines. One tiny advice can be to name the to be tested instance as underTest. It helps to keep the overview among all the mocked services, test data creation and assertions.


class DateConverterTest {

    private DateConverter underTest;

    @BeforeEach
    void setUp() {
        underTest = new DateConverter();
    }

    @Test
    void whenConvertGiven3DaysThenReturn3DaysAgo() {
        final var input = LocalDateTime.now().minusDays(3);
        final var expectation = "3 days ago";
        final var result = underTest.convert(input);

        assertThat(result, is(expectation));
    }
}


testing of private methods


recently i asked a few of my colleagues what they do if the private method hides some logic, but needs to be tested. some told me that they implicitly test it through the public method. this approach is not that bad, but it can lead to more complexity of the main tests and could cost people more time trying to understand your intent. some other workmates showed me that they test private methods using reflection. in my opinion this is a bad idea. if you have to rename a method. some of the modern development tools are capable of renaming the reflection call, too. but if the tool isn't aware of that, there a risk of running in a runtime error. another reason is that the reflection call is hard to read and buys you some complexity in a test.


in my view, you should extract the code of the private method to a new class and make the newly generated method public. if the method was small, so will be the test. if you don't want to move it to a new class, you can increase the visibility of the method from private to default or protected, so the test can access it easily.


use parameterized tests


sometimes your tests vary in the test input only. you don't have to write 10 times the same test only to switch the inbound parameter value. this approach increases code redundancy of your test class and bloats it needlessly.


you can work with a parameterized test, where the testing framework lets you iterate over the same test with different input. with this strategy you keep the complexity of the test class steady at a low level.


class StringUtilsTest {

    private final StringUtils underTest = new StringUtils();

    @ParameterizedTest
    @ValueSource(strings = {"1", "2", "123", "0", "-1"})
    void name(final String input) {
        final var expectation = true;
        final var result = underTest.isNumber(input);

        assertThat(result, is(expectation));
    }
}


usage of existing mocking frameworks


a lot of modern frameworks and libraries let you work with interfaces, so you dont have to worry about the implementation details. but then it comes to testing, you don't always have access to the implementation and start implementing the interface with simple behavior to fit your testing scenario.


this approach costs time and can be minimized using existing mocking frameworks. in java the most well known libraries are mockito, jmockit and easymock. if you aren't interested in the return value or if it is a void method, you can create a mock with one line. if the mocked method should return something specific, another extra line will do the job.


@Test
void whenCheckAndGetCreateGivenEntityWithoutPlanIdThenReturnNewLocation() {
    final var createdAt = LocalDateTime.now();
    final var input = PlanDto.of("trainingPlan1", createdAt);
    final var expectations = Plan.of("trainingPlan1", createdAt);
    expectations.setPlanId("planId1");

    Mockito.when(planEao.insert(Mockito.any(Plan.class)))
            .thenReturn(expectations);

    final var result = underTest.checkAndCreate(input);

    assertThat(result.getPath(), is("/planId1"));
}


creating file/live templates


many tasks as a developer are recurring. you create lots of entities, dtos, tests, assertions and mocking rules. intellij, eclipse and other modern developer tools give the opportunity to create file and/or live templates. instead of copy pasting code from other classes or writing it by hand, you can create templates. this will save you time and gives you consistent code structure. some development environments already have build in templates to create test classes and test methods. i usually enhance this templates by adding more static imports for assertion matchers and the mocking library.


conclusion


i hope you can agree in some of my suggestions and try them out in your current or next project. the most tactics don't guarantee that your code will stay clean and will be free of complexity, but it has the chance to buy you some time focusing on the important tasks instead of struggling with some sideshows. if you have any additions, please let me know in the comments, i appreciate it.

Recent Posts

See All