All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kiwiproject.test.junit.jupiter.Jdbi3DaoExtension Maven / Gradle / Ivy

package org.kiwiproject.test.junit.jupiter;

import static java.util.Objects.isNull;
import static org.kiwiproject.base.KiwiPreconditions.requireNotNull;
import static org.kiwiproject.test.junit.jupiter.Jdbi3Helpers.buildJdbi;
import static org.kiwiproject.test.junit.jupiter.Jdbi3Helpers.configureSqlLogger;

import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
import lombok.extern.slf4j.Slf4j;
import org.jdbi.v3.core.ConnectionFactory;
import org.jdbi.v3.core.Handle;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.spi.JdbiPlugin;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.List;

/**
 * A JUnit Jupiter {@link org.junit.jupiter.api.extension.Extension Extension} to easily test JDBI 3-based DAOs in
 * against any database and using transaction rollback to make sure tests never commit to the database.
 * 

* You must supply the {@code daoType} and one of three methods for obtaining a database {@link Connection}: * (1) a {@link DataSource}, (2) a JDBI {@link ConnectionFactory}, or * (3) the JDBC URL, username, and password. *

* Before each test, sets up a transaction. After each test completes, rolls the transaction back. *

* Using the builder, you can optionally specify {@link JdbiPlugin} instances to install. Note that this extension * always installs the {@link org.jdbi.v3.sqlobject.SqlObjectPlugin SqlObjectPlugin}. * * @param the DAO type */ @Slf4j public class Jdbi3DaoExtension implements BeforeEachCallback, AfterEachCallback { /** * The type of DAO (required) */ @Getter private final Class daoType; /** * The {@link Jdbi} instance created by this extension. Not intended for you to mess with, but provided "just in * case" there is some good reason to do so. */ @Getter private final Jdbi jdbi; /** * The {@link Handle} instance created by this extension. Intended to be used when creating sample data required by * unit tests. You should generally otherwise not be messing with it. */ @Getter private Handle handle; /** * The DAO test subject, attached to the {@link Handle} using {@link Handle#attach(Class)} and executing within * a transaction. *

* Obviously intended to be used in tests. */ @Getter private T dao; /** * The DAO type is always required. *

* Exactly one of the following must be supplied: * (1) url, username, password, (2) connectionFactory, or (3) dataSource. *

* Optionally, you can specify a custom name for the SLF4J Logger as well as {@link JdbiPlugin} instances to * install. The {@link org.jdbi.v3.sqlobject.SqlObjectPlugin} is always installed. * * @param url The JDBC URL; paired with username & password (optional, defaults to null) * @param username The JDBC username; paired with url & password (optional, defaults to null) * @param password The JDBC password; paired with url & username (optional, defaults to null) * @param connectionFactory The JDBI {@link ConnectionFactory} (optional, defaults to null) * @param dataSource The JDBC {@link DataSource} (optional, defaults to null) * @param slf4jLoggerName The SLF4J {@link org.slf4j.Logger} name (optional, defaults to the FQCN of {@link Jdbi} * @param daoType The type of JDBI 3 DAO * @param plugins a list containing the JDBI 3 plugins to install * (a {@link org.jdbi.v3.sqlobject.SqlObjectPlugin SqlObjectPlugin} is always installed) * @implNote At present the {@link org.jdbi.v3.core.statement.SqlLogger} for the given {@code slf4jLoggerName} logs * at TRACE level only. */ @SuppressWarnings("java:S107") // builder-annotated constructors are an exception to the "too many parameters" rule @Builder private Jdbi3DaoExtension(String url, String username, String password, ConnectionFactory connectionFactory, DataSource dataSource, String slf4jLoggerName, Class daoType, @Singular List plugins) { LOG.trace("A new {} is being instantiated", Jdbi3DaoExtension.class.getSimpleName()); this.daoType = requireNotNull(daoType, "Must specify the DAO type"); var nonNullPlugins = isNull(plugins) ? List.of() : plugins; this.jdbi = buildJdbi(dataSource, connectionFactory, url, username, password, nonNullPlugins); configureSqlLogger(jdbi, slf4jLoggerName); } /** * Create a DAO attached to the {@link Handle} and assigns it; it is accessible to tests via {@code getDao()}. * Begins a transaction. * * @param context the extension context * @see Jdbi#open() * @see Handle#attach(Class) * @see Handle#begin() */ @Override public void beforeEach(ExtensionContext context) { LOG.trace("Setting up for JDBI DAO test"); LOG.trace("Opening handle"); handle = jdbi.open(); LOG.trace("Txn isolation level: {}", handle.getTransactionIsolationLevel()); LOG.trace("Attach type {} to handle", daoType); dao = handle.attach(daoType); LOG.trace("Beginning transaction"); handle.begin(); LOG.trace("Done setting up for JDBI DAO test"); } /** * Rolls back the transaction on the {@link Handle} and closes it. * * @param context the extension context * @see Handle#rollback() * @see Handle#close() */ @Override public void afterEach(ExtensionContext context) { Jdbi3Helpers.rollbackAndClose(handle, LOG); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy