In tests
The JUnit extension runs each test as a trace and fails a test with an N+1, duplicate queries or too many queries.
The JUnit extension turns what Tracepanel finds into test failures, so an
N+1 query fails CI before it reaches production. It comes with the
dev.tracepanel:tracepanel-junit library and needs JUnit Jupiter 5.12 or
later, JUnit 6 included.
Turn it on
Add the library to the test dependencies. With Gradle:
// build.gradle.kts
dependencies {
testImplementation("dev.tracepanel:tracepanel-junit:0.9.0")
}With Maven:
<dependency>
<groupId>dev.tracepanel</groupId>
<artifactId>tracepanel-junit</artifactId>
<version>0.9.0</version>
<scope>test</scope>
</dependency>Then put @Tracepanel on a test class, or turn the extension on for every
test class with JUnit's extension autodetection, which finds it in the jar:
# src/test/resources/junit-platform.properties
junit.jupiter.extensions.autodetection.enabled=truetracepanel.failOn sets the kinds of issues that fail a test, in the same
file or as a system property:
tracepanel.failOn=n+1, duplicates, slow-sqlIt takes the issue names that has: takes in a search, such
as slow-sql or error. The default is n+1 and duplicates.
The extension turns Tracepanel on in the test JVM, as development does, with
the widget off, and starts the project's agent when none is running. Spring
Boot's test slices, @WebMvcTest, @JdbcTest, @DataJpaTest,
@DataJdbcTest and @JooqTest, record their requests and queries as a
@SpringBootTest does. Outside Spring, wrap the DataSource your tests use
with Jdbc.dataSource(dataSource, "default").
What a test records
Each test runs as a test trace named after its class and method. A request
the test makes of the app, with MockMvc, TestRestTemplate,
WebTestClient or Ktor's testApplication client, is a trace of its own,
linked to the test's in the console. @BeforeAll and @BeforeEach run
outside the test's trace, so seeding the database doesn't count. A
parameterized test is a trace for each of its runs.
When the test passes, the agent looks at its traces. An issue of a kind in
tracepanel.failOn fails the test with the issue, its query, the line of
code that ran it and a link to the trace:
AssertionFailedError: tracepanel found 1 issue in this test:
N+1: 4 similar queries · SELECT author
SELECT name FROM author WHERE id = ?
at src/main/java/shop/Books.java:20 in lambda$books$0
in GET /books → http://127.0.0.1:52068/#/t/01M60NB74Q6T9AXZGMFJ7V8FB1
Load the related rows with the parent rows (a join, or one batched query) ...
The test's trace: http://127.0.0.1:52068/#/t/01M60NB74J4Q5VDHCJD1Z4T1J7
Allowed here with @Tracepanel(allow = "n+1").Change the rules for a test
@Tracepanel on a test method or class changes the rules for its tests. A
method's own rules come first, then those of its class and the classes
around it:
| Rule | What it does |
|---|---|
maxQueries | Fails the test when it and its requests make more queries than this. |
failOn | Replaces the kinds of issues that fail the test. |
allow | Takes kinds of issues out of those that fail the test. |
A misspelled kind fails the test that names it, before it runs.
A test method can take its trace as a TestTrace parameter, or get it from
testTrace(): queries() counts the queries so far, its requests'
included, and sql() lists them, each with its statement and the line of
code that ran it. issues() lists what the agent found in the test's
requests so far, and url() is the test's trace in the console, or null
without an agent. maxQueries(n, block) fails when the block makes more
than n queries:
import static dev.tracepanel.junit.TracepanelExtension.maxQueries;
@Tracepanel
@SpringBootTest
@AutoConfigureMockMvc
class FeedTest {
@Autowired
MockMvc mvc;
@Test
@Tracepanel(maxQueries = 10, allow = "n+1")
void feed(TestTrace trace) throws Exception {
mvc.perform(get("/feed")).andExpect(status().isOk());
assertThat(trace.queries()).isLessThanOrEqualTo(10);
maxQueries(2, () -> mvc.perform(get("/feed?page=2")));
}
}Without an agent
With TRACEPANEL_AGENT=off, or on a platform the agent isn't built for, the
tests run as usual and one warning says that issues aren't checked. Query
counts need no agent, so maxQueries still works. TRACEPANEL_ENABLED=0
turns the extension off.