A load test style guide for JUnit (and AssertJ).
AssertJ assertions are ‘hard’ by default - the first failing assertion stops the test. This is unhelpful when asserting on a POJO, so use SoftAssertions
instead.
@Category(LoadTest.class)
public class PetStoreTest {
private static final LoadTester loadTester = LoadTesterFactory.getLoadTester();
@Test
public void shouldFindPets() {
final Result result = loadTester.run(requests);
assertSoftly(s -> {
s.assertThat(result.getResponseTime().getPercentile(75)).as("p75 Response Time").isLessThanOrEqualTo(Duration.ofMillis(500));
s.assertThat(result.getPercentOk()).as("Percent OK").isGreaterThanOrEqualTo(99.99);
// and so on...
});
}
}
Follow the AssertJ custom assertions guide to write assertions on Result
which are relevant to your SLO.
Note: The custom assertions class must be proxied through SoftAssertions
for the same reason as normal assertions.
This style might be more readable if your SLO has many requirements.
@Category(LoadTest.class)
public class FindPetsTest {
private static final List<Request> requests = Arrays.asList(Request.get("/"), Request.get("/pets"));
private static final Supplier<Result> result = LazyLoadTester.run(requests);
@Test
public void shouldSatisfyP75ResponseTimeThreshold() {
assertThat(result.get().getResponseTime().getPercentile(75)).isLessThanOrEqualTo(Duration.ofMillis(500));
}
@Test
public void shouldSatisfyPercentOkThreshold() {
assertThat(result.get().getPercentOk()).isGreaterThanOrEqualTo(99.99);
}
}
To use this style you need to defer load test execution until a test requires it, to avoid blocking test suite initialization while the LoadTester executes.
Java does not yet support lazy evaluation, so we must emulate this with a utility function that returns a Supplier<Result>
. Here is an example:
public class LazyLoadTester {
public static Supplier<Result> run(List<Request> requests) {
// The memoize step is critical: it ensures that a given LoadTest4jSupplier is only run ONCE.
// Either write your own memoizer, or use your favorite library.
return Suppliers.memoize(new Loadtest4jSupplier(requests));
}
private static class Loadtest4jSupplier implements Supplier<Result> {
private static final LoadTester loadTester = LoadTesterFactory.getLoadTester();
private final List<Request> requests;
private Loadtest4jSupplier(List<Request> requests) {
this.requests = requests;
}
@Override
public Result get() {
return loadTester.run(requests);
}
}
}