Skip to main content

Tests

Three test suites are available, each started through its own class:

@Suite
@IncludeTags(Tags.UNIT)
@SelectPackages("de.hsrm.sls.subato")
public class UnitTestSuite {}

@Suite
@IncludeTags(Tags.INTEGRATION)
@ExcludeTags(Tags.EXTERNAL_DEPENDENCY)
@SelectPackages("de.hsrm.sls.subato")
public class IntegrationTestSuite {}

@Suite
@IncludeTags({Tags.INTEGRATION, Tags.EXTERNAL_DEPENDENCY})
@SelectPackages("de.hsrm.sls.subato")
public class ExternalIntegrationTestSuite {}

Test classes are tagged accordingly so that they run automatically as part of the matching suite. Tags.EXTERNAL_DEPENDENCY marks classes that communicate with external systems and may therefore need extra configuration to run. For example, TaskPoolRepositoryManagerTests is tagged with Tags.EXTERNAL_DEPENDENCY because it depends on a VCS with valid Git credentials set through environment variables for cloning (private) repositories.

Fixtures

src/test/resources/data.sql

The test environment has its own configuration (src/test/resources/application-testsuite.yml) that overrides src/main/resources/application.yml and sets up the integration tests to run an instance of Subato against an H2 in-memory database. The database is then initialized from src/test/resources/data.sql when the test runner starts.

A test that modifies the database must be annotated with @Transactional. Then, the change is rolled back at the end of the test case, so the next one starts from the fresh state defined in src/test/resources/data.sql. For example, UpdateCourseTest checks that updating a course through the API works correctly. As this should cause a write to the database, the class is annotated with @Transactional.

subato2-data

Beyond the database, some integration tests need tasks and solutions to be present on the file system. These fixtures live under src/test/resources/testfiles/subato2-data and must match the metadata in data.sql. Because running a test case can introduce changes to the subato2-data directory, it should be reset before each test case as follows:

@BeforeAll
public static void beforeAll(@Autowired PrepareSubatoDataDir dataDirCleanup, @Autowired SubatoTmpDirCleanup tmpDirCleanup) {
dataDirCleanup.exec();
tmpDirCleanup.exec();
}

Testing Endpoints

Every endpoint should be tested to verify that authentication and authorization are enforced by policies. As testing individual policies in isolation is often unnecessarily cumbersome, each endpoint should instead get a test class with several integration tests. This approach exercises all applicable policies together, ensuring that the endpoint is sufficiently protected. These classes should also cover various edge cases to verify that the endpoint does what it is supposed to. See UpdateCourseTest for an example.

To set the user context in a MockMvc integration test, the test method should be annotated with @WithUserDetails, passing an username of an user that was declared in src/test/resources/data.sql (e.g. U_STAFF_C1A). Usernames are encoded using the following schema:

  • U: a user
  • STAFF: the staff role
  • C1A: Admin permission in course 1

The declared users should be reused where possible to keep the test suite maintainable.