Starter for using jOOQ to access SQL databases with JDBC, providing auto-configuration and Spring integration
—
Testing support for jOOQ-based components providing slice testing capabilities that focus only on jOOQ operations while excluding non-relevant Spring Boot features.
Main test annotation for jOOQ slice testing that configures only jOOQ-related components and provides transactional test execution.
/**
* Annotation for a jOOQ test that focuses only on jOOQ-based components.
* Using this annotation enables auto-configuration relevant to jOOQ tests only.
* Component scanning is configured to skip regular components and configuration properties.
* By default uses the configured database, but can be overridden with @AutoConfigureTestDatabase.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(JooqTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(JooqTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureJooq
@ImportAutoConfiguration
public @interface JooqTest {
/**
* Properties in form key=value that should be added to the Spring Environment before the test runs
* @return the properties to add
*/
String[] properties() default {};
/**
* Determines if default filtering should be used with @SpringBootApplication
* By default no beans are included
* @return if default filters should be used
*/
boolean useDefaultFilters() default true;
/**
* A set of include filters which can be used to add otherwise filtered beans to the application context
* @return include filters to apply
*/
Filter[] includeFilters() default {};
/**
* A set of exclude filters which can be used to filter beans that would otherwise be added to the application context
* @return exclude filters to apply
*/
Filter[] excludeFilters() default {};
/**
* Auto-configuration exclusions that should be applied for this test
* @return auto-configuration exclusions to apply
*/
@AliasFor(annotation = ImportAutoConfiguration.class, attribute = "exclude")
Class<?>[] excludeAutoConfiguration() default {};
}Auto-configuration imports for typical jOOQ tests. Most tests should use @JooqTest rather than this annotation directly.
/**
* Auto-configuration imports for typical jOOQ tests
* Most tests should consider using @JooqTest rather than using this annotation directly
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
public @interface AutoConfigureJooq {
}Usage Examples:
@JooqTest
class UserRepositoryTest {
@Autowired
private DSLContext dsl;
@Test
void shouldFindUserById() {
// Insert test data
dsl.insertInto(USER)
.set(USER.ID, 1L)
.set(USER.NAME, "John Doe")
.set(USER.EMAIL, "john@example.com")
.execute();
// Test query
User user = dsl.selectFrom(USER)
.where(USER.ID.eq(1L))
.fetchOneInto(User.class);
assertThat(user.getName()).isEqualTo("John Doe");
assertThat(user.getEmail()).isEqualTo("john@example.com");
}
@Test
void shouldUpdateUserEmail() {
// Setup
dsl.insertInto(USER)
.set(USER.ID, 1L)
.set(USER.NAME, "Jane Doe")
.set(USER.EMAIL, "jane@old.com")
.execute();
// Update
int updated = dsl.update(USER)
.set(USER.EMAIL, "jane@new.com")
.where(USER.ID.eq(1L))
.execute();
// Verify
assertThat(updated).isEqualTo(1);
String newEmail = dsl.select(USER.EMAIL)
.from(USER)
.where(USER.ID.eq(1L))
.fetchOne(USER.EMAIL);
assertThat(newEmail).isEqualTo("jane@new.com");
}
}@JooqTest(properties = {
"spring.jooq.sql-dialect=H2",
"logging.level.org.jooq=DEBUG"
})
class ProductRepositoryTest {
@Autowired
private DSLContext dsl;
@Test
void shouldCreateProduct() {
// Test will run with H2 dialect and jOOQ debug logging enabled
dsl.insertInto(PRODUCT)
.set(PRODUCT.NAME, "Test Product")
.set(PRODUCT.PRICE, new BigDecimal("29.99"))
.execute();
Integer count = dsl.selectCount()
.from(PRODUCT)
.fetchOne(0, Integer.class);
assertThat(count).isEqualTo(1);
}
}@JooqTest
@TestConfiguration
class OrderServiceTest {
@Autowired
private DSLContext dsl;
// Custom configuration for test
@TestConfiguration
static class Config {
@Bean
@Primary
public DefaultConfigurationCustomizer testCustomizer() {
return configuration -> {
// Add test-specific configuration
Settings settings = new Settings();
settings.withExecuteLogging(true);
configuration.set(settings);
};
}
}
@Test
void shouldProcessOrder() {
// Insert test order
dsl.insertInto(ORDER)
.set(ORDER.ID, 1L)
.set(ORDER.CUSTOMER_ID, 123L)
.set(ORDER.TOTAL, new BigDecimal("99.99"))
.execute();
// Verify order exists
Order order = dsl.selectFrom(ORDER)
.where(ORDER.ID.eq(1L))
.fetchOneInto(Order.class);
assertThat(order).isNotNull();
assertThat(order.getTotal()).isEqualByComparingTo(new BigDecimal("99.99"));
}
}@JooqTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
class InMemoryDatabaseTest {
@Autowired
private DSLContext dsl;
@Test
void shouldWorkWithEmbeddedDatabase() {
// This test will use an embedded in-memory database
// instead of the configured production database
dsl.insertInto(CUSTOMER)
.set(CUSTOMER.NAME, "Test Customer")
.set(CUSTOMER.EMAIL, "test@example.com")
.execute();
List<Customer> customers = dsl.selectFrom(CUSTOMER)
.fetchInto(Customer.class);
assertThat(customers).hasSize(1);
assertThat(customers.get(0).getName()).isEqualTo("Test Customer");
}
}@JooqTest
class ExceptionHandlingTest {
@Autowired
private DSLContext dsl;
@Test
void shouldHandleDuplicateKeyException() {
// Insert initial record
dsl.insertInto(USER)
.set(USER.EMAIL, "unique@example.com")
.set(USER.NAME, "First User")
.execute();
// Attempt duplicate insert - should throw Spring exception
assertThatThrownBy(() ->
dsl.insertInto(USER)
.set(USER.EMAIL, "unique@example.com") // Duplicate email
.set(USER.NAME, "Second User")
.execute()
).isInstanceOf(DuplicateKeyException.class);
}
@Test
void shouldHandleConstraintViolation() {
// Test constraint violations are properly translated
assertThatThrownBy(() ->
dsl.insertInto(USER)
.set(USER.EMAIL, "invalid-email-format") // Violates email constraint
.set(USER.NAME, "Test User")
.execute()
).isInstanceOf(DataIntegrityViolationException.class);
}
}Test context bootstrapper specifically for jOOQ tests that ensures proper test slice configuration.
/**
* BootstrapContext for @JooqTest based tests
* Ensures proper test slice configuration for jOOQ-only testing
*/
class JooqTestContextBootstrapper extends SpringBootTestContextBootstrapper {
/**
* Get the configuration classes for the test context
* @param testClass the test class being bootstrapped
* @return array of configuration classes
*/
@Override
protected Class<?>[] getOrFindConfigurationClasses(Class<?> testClass);
}Type filter that excludes non-jOOQ components during test context loading to ensure slice testing focuses only on jOOQ functionality.
/**
* TypeExcludeFilter for @JooqTest
* Excludes non-jOOQ components to ensure slice testing
*/
class JooqTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter {
/**
* Constructor taking the test class
* @param testClass the test class to configure filtering for
*/
JooqTypeExcludeFilter(Class<?> testClass);
}@JooqTest annotated tests run within a transaction by default@AutoConfigureTestDatabase for embedded databasesInstall with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-jooq