Skip to content

Workshop #4: Unit and IT testing

Marcin Zielonka edited this page Nov 30, 2022 · 12 revisions

About πŸ“

Workshop #3 is about testing our application including the REST API we exposed. Therefore, you can find here topics such as:

  • Groovy language for writing tests
  • Spock test framework
  • unit, integration and end-to-end testing
  • mocking
  • API testing in Spring

This workshop

Source code πŸ—ƒ

You can find the source code that represents the status of the project after this workshop - TBA


Testing application

Unit tests

Unit test is a type of software test where a single part of the software is verified. The purpose is to validate that each unit of the software code performs as expected. Unit tests are written by developers during the coding phase and exist along with the application code (e.g. in the separate test package, etc.). Such tests isolate the particular section of code and verify its correctness. Therefore, it is much easier to build stable applications from such tested components knowing that these parts themselves are working properly.

Integration tests

Integration tests (also: IT tests) verify the correctness of integration between components (that are already fully tested with unit tests). Therefore, they focus on checking if there is proper communication between them, if they are not clashing together, etc. They are also slower than unit tests because usually they need to have some application context brought up (e.g. Spring context), etc.

End-to-end tests

End-to-end tests (also: E2E tests) focus on verifying the full application workflow - from the beginning to the end. They are much heavier than integration or unit tests because they can cover several services that are communicating with each other to implement some flow.

Performance tests

Performance tests (also: load tests) focus on verifying if the developed application works properly under high traffic, for example. Therefore, such tests can generate fake traffic (e.g. in form of HTTP requests made to the API) and check if the application responds correctly and fast enough.

More and more tests...

There are also other types of testing you can provide during designing the application. However, the tests mentioned above are the most common ones which you can find in almost every project.

More about different types of tests: https://www.geeksforgeeks.org/types-software-testing/


Groovy

According to the official site, Groovy is a powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities, for the Java platform aimed at improving developer productivity thanks to a concise, familiar and easy to learn syntax. It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming

Why is it perfect for testing?

Groovy provides some features that make writing tests much easier and quicker.

Default imports

Default imports are the imports that Groovy language provides by default. The below ones are added automatically:

  • import java.lang.*
  • import java.util.*
  • import java.io.*
  • import java.net.*
  • import groovy.lang.*
  • import groovy.util.*
  • import java.math.BigInteger
  • import java.math.BigDecimal

No semicolons required

πŸŽ₯ This part will be available after workshops πŸŽ₯

Auto-generated getters and setters

πŸŽ₯ This part will be available after workshops πŸŽ₯

Support for collections

πŸŽ₯ This part will be available after workshops πŸŽ₯

GStrings

πŸŽ₯ This part will be available after workshops πŸŽ₯

Safe navigation operator

πŸŽ₯ This part will be available after workshops πŸŽ₯

Writing tests with Groovy

Groovy is perfect for writing tests in your application thanks to its syntax and the features it provides. However, these are only words - so let's compare the sample test written in Java using JUnit5 and the same one but in Groovy using Spock (more about Spock in the next chapter).

Let's assume we have a sample component that verifies if a number is positive.

public class NumberUtils {

    public static boolean isPositive(int number) {
        return number > 0;
    }
}

The test for the isPositive(int number) method written in Java using JUnit5:

public class NumberUtilsTest {

    @Test
    void testIsPositive() {
        assertFalse(NumberUtils.isPositive(-1));
        assertFalse(NumberUtils.isPositive(0));
        assertTrue(NumberUtils.isPositive(5));
    }

}

The same test but written in Groovy using Spock:

class NumerUtilsSpec extends Specification {

    void "should test if number is positive"() {
        expect:
        NumberUtils.isPositive(number) == expectedResult

        where:
        number || expectedResult
        -1     || false
        0      || false
        5      || true
    }

}

Documentation

You can find more information about Groovy in the official documentation that can be found here:

https://docs.groovy-lang.org/next/html/documentation/


Spock

Sample test in Spock

Key features

Fixture methods

πŸŽ₯ This part will be available after workshops πŸŽ₯

Blocks

πŸŽ₯ This part will be available after workshops πŸŽ₯

Implicit assertions

πŸŽ₯ This part will be available after workshops πŸŽ₯

Mocking

πŸŽ₯ This part will be available after workshops πŸŽ₯

Data pipes

πŸŽ₯ This part will be available after workshops πŸŽ₯

Datatables

πŸŽ₯ This part will be available after workshops πŸŽ₯

Documentation

You can find more information about Spock in the official documentation that can be found here:

https://spockframework.org/spock/docs/1.1/all_in_one.html


Let's write some tests

Setup

First, we need to add proper dependencies to have both Groovy and Spock supported in our Gradle project (via build.gradle file).

// Spock
implementation platform('org.apache.groovy:groovy-bom:4.0.5')
implementation 'org.apache.groovy:groovy'

testImplementation platform("org.spockframework:spock-bom:2.3-groovy-4.0")
testImplementation "org.spockframework:spock-core"
testImplementation "org.spockframework:spock-junit4"

We also want to write some integration tests that we want to have in different source set. Therefore, let's declare it (also in build.gradle file):

sourceSets {
    integrationTest {
        compileClasspath += sourceSets.main.output
        runtimeClasspath += sourceSets.main.output
        compileClasspath += sourceSets.test.output
        runtimeClasspath += sourceSets.test.output
    }
}

configurations {
    integrationTestImplementation.extendsFrom implementation
    integrationTestImplementation.extendsFrom testImplementation
    integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}

Next step is to register new task for running integration tests:

tasks.register('integrationTest', Test) {
    testClassesDirs = sourceSets.integrationTest.output.classesDirs
    classpath = sourceSets.integrationTest.runtimeClasspath

    shouldRunAfter test
    useJUnitPlatform()
}

check.dependsOn integrationTest

Lastly, we need to provide one additional dependency that will be used only in integration tests and it is related to IT tests in Spring Boot.

integrationTestImplementation "org.spockframework:spock-spring"

Unit tests

πŸŽ₯ This part will be available after workshops πŸŽ₯

Integration tests

πŸŽ₯ This part will be available after workshops πŸŽ₯