CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-dropwizard--dropwizard-testing

Comprehensive testing framework and utilities for Dropwizard applications including JUnit 5 extensions, resource testing, and DAO helpers

Pending
Overview
Eval results
Files

dao-testing.mddocs/

DAO Testing

Database testing utilities with automated Hibernate SessionFactory management, transaction handling, and H2 in-memory database setup. Essential for testing data access layers with isolated database environments.

Capabilities

DAOTest

Abstract base class providing comprehensive database testing environment with Hibernate SessionFactory management, transaction handling, and configurable database setup.

/**
 * Abstract base class for DAO testing with Hibernate support
 */
public abstract class DAOTest {
    
    /**
     * Get the Hibernate SessionFactory for database operations
     * @return SessionFactory instance for creating sessions
     */
    public SessionFactory getSessionFactory();
    
    /**
     * Execute code within a database transaction with return value
     * Automatically handles transaction commit/rollback
     * @param call Callable to execute within transaction
     * @return Result of the callable execution
     * @throws Exception if the callable execution fails
     */
    public <T> T inTransaction(Callable<T> call) throws Exception;
    
    /**
     * Execute code within a database transaction without return value
     * Automatically handles transaction commit/rollback
     * @param action Runnable to execute within transaction
     * @throws Exception if the action execution fails
     */
    public void inTransaction(Runnable action) throws Exception;
    
    // Lifecycle methods
    /**
     * Initialize the database testing environment before tests
     * Sets up SessionFactory, creates schema, etc.
     * @throws Exception if initialization fails
     */
    public void before() throws Exception;
    
    /**
     * Cleanup the database testing environment after tests
     * Closes SessionFactory, drops schema, etc.
     * @throws Exception if cleanup fails
     */
    public void after() throws Exception;
}

DAOTest.Builder

Fluent builder for configuring DAOTest instances with database connection settings, Hibernate properties, entity classes, and custom configuration.

/**
 * Abstract builder for DAOTest configuration
 * @param <B> Builder type for method chaining
 */
public static abstract class Builder<B extends Builder<B>> {
    
    // Database connection configuration
    /**
     * Set the database URL
     * @param url JDBC URL (default: H2 in-memory database)
     * @return Builder instance for chaining
     */
    public B setUrl(String url);
    
    /**
     * Set the database username
     * @param username Database username (default: "sa")
     * @return Builder instance for chaining
     */
    public B setUsername(String username);
    
    /**
     * Set the database password
     * @param password Database password (default: empty)
     * @return Builder instance for chaining
     */
    public B setPassword(String password);
    
    // JDBC driver configuration
    /**
     * Set the JDBC driver class
     * @param driver JDBC driver class (default: H2 driver)
     * @return Builder instance for chaining
     */
    public B setDriver(Class<? extends Driver> driver);
    
    /**
     * Set the JDBC driver class by name
     * @param driver JDBC driver class name
     * @return Builder instance for chaining
     */
    public B setDriver(String driver);
    
    // Hibernate configuration
    /**
     * Set Hibernate DDL auto mode
     * @param hbm2ddlAuto DDL mode (create-drop, create, update, validate, none)
     * @return Builder instance for chaining
     */
    public B setHbm2DdlAuto(String hbm2ddlAuto);
    
    /**
     * Enable/disable SQL statement logging
     * @param showSql true to log SQL statements, false otherwise
     * @return Builder instance for chaining
     */
    public B setShowSql(boolean showSql);
    
    /**
     * Enable/disable SQL comments in logged statements
     * @param useSqlComments true to include comments, false otherwise
     * @return Builder instance for chaining
     */
    public B useSqlComments(boolean useSqlComments);
    
    /**
     * Enable/disable Logback logging bootstrap
     * @param value true to bootstrap logging, false otherwise
     * @return Builder instance for chaining
     */
    public B bootstrapLogging(boolean value);
    
    // Entity configuration
    /**
     * Add an entity class to the Hibernate configuration
     * @param entityClass JPA entity class to register
     * @return Builder instance for chaining
     */
    public B addEntityClass(Class<?> entityClass);
    
    // Custom properties
    /**
     * Set a custom Hibernate property
     * @param key Property name
     * @param value Property value
     * @return Builder instance for chaining
     */
    public B setProperty(String key, String value);
    
    /**
     * Add custom configuration logic
     * @param configurationCustomizer Consumer for custom Configuration setup
     * @return Builder instance for chaining
     */
    public B customizeConfiguration(Consumer<Configuration> configurationCustomizer);
    
    /**
     * Build the configured DAOTest instance
     * @return Configured DAOTest instance ready for testing
     */
    public abstract DAOTest build();
}

Usage Examples:

// Basic DAO testing with H2 in-memory database
DAOTest daoTest = DAOTestExtension.newBuilder()
    .addEntityClass(User.class)
    .addEntityClass(Order.class)
    .build();

@BeforeEach
public void setUp() throws Exception {
    daoTest.before();
}

@AfterEach
public void tearDown() throws Exception {
    daoTest.after();
}

@Test
public void testUserDAO() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    // Test data creation
    User user = daoTest.inTransaction(() -> {
        User newUser = new User("Alice", "alice@example.com");
        return userDAO.save(newUser);
    });
    
    assertThat(user.getId()).isNotNull();
    
    // Test data retrieval
    Optional<User> foundUser = daoTest.inTransaction(() -> 
        userDAO.findById(user.getId())
    );
    
    assertThat(foundUser).isPresent();
    assertThat(foundUser.get().getName()).isEqualTo("Alice");
}

// Custom database configuration
DAOTest daoTest = DAOTestExtension.newBuilder()
    .setUrl("jdbc:postgresql://localhost:5432/test_db")
    .setUsername("test_user")
    .setPassword("test_password")
    .setDriver("org.postgresql.Driver")
    .addEntityClass(User.class)
    .addEntityClass(Order.class)
    .setHbm2DdlAuto("create-drop")
    .setShowSql(true)
    .build();

// Multiple entity classes and custom properties
DAOTest daoTest = DAOTestExtension.newBuilder()
    .addEntityClass(User.class)
    .addEntityClass(Order.class) 
    .addEntityClass(Product.class)
    .addEntityClass(Category.class)
    .setProperty("hibernate.cache.use_second_level_cache", "false")
    .setProperty("hibernate.cache.use_query_cache", "false")
    .setProperty("hibernate.jdbc.batch_size", "25")
    .useSqlComments(true)
    .build();

// Custom configuration with advanced Hibernate settings
DAOTest daoTest = DAOTestExtension.newBuilder()
    .addEntityClass(User.class)
    .customizeConfiguration(config -> {
        config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        config.setProperty("hibernate.connection.isolation", "2");
        config.setProperty("hibernate.order_inserts", "true");
        config.setProperty("hibernate.order_updates", "true");
    })
    .build();

// Testing with transaction rollback for data isolation
@Test
public void testTransactionRollback() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    try {
        daoTest.inTransaction(() -> {
            User user = new User("Bob", "bob@example.com");
            userDAO.save(user);
            
            // Simulate error that causes rollback
            throw new RuntimeException("Test rollback");
        });
    } catch (RuntimeException e) {
        // Expected exception
    }
    
    // Verify rollback occurred
    List<User> users = daoTest.inTransaction(() -> userDAO.findAll());
    assertThat(users).isEmpty();
}

// Complex DAO testing with relationships
@Test
public void testUserOrderRelationship() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    OrderDAO orderDAO = new OrderDAO(daoTest.getSessionFactory());
    
    User user = daoTest.inTransaction(() -> {
        User newUser = new User("Charlie", "charlie@example.com");
        return userDAO.save(newUser);
    });
    
    Order order = daoTest.inTransaction(() -> {
        Order newOrder = new Order(user, "Test Product", new BigDecimal("99.99"));
        return orderDAO.save(newOrder);
    });
    
    // Test relationship loading
    User userWithOrders = daoTest.inTransaction(() -> 
        userDAO.findByIdWithOrders(user.getId())
    );
    
    assertThat(userWithOrders.getOrders()).hasSize(1);
    assertThat(userWithOrders.getOrders().get(0).getId()).isEqualTo(order.getId());
}

// Testing DAO queries and pagination
@Test
public void testDAOQueries() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    // Create test data
    daoTest.inTransaction(() -> {
        for (int i = 1; i <= 10; i++) {
            User user = new User("User" + i, "user" + i + "@example.com");
            userDAO.save(user);
        }
    });
    
    // Test pagination
    List<User> firstPage = daoTest.inTransaction(() -> 
        userDAO.findAll(0, 5)
    );
    assertThat(firstPage).hasSize(5);
    
    List<User> secondPage = daoTest.inTransaction(() -> 
        userDAO.findAll(5, 5)
    );
    assertThat(secondPage).hasSize(5);
    
    // Test search queries
    List<User> searchResults = daoTest.inTransaction(() -> 
        userDAO.findByEmailContaining("user1")
    );
    assertThat(searchResults).hasSize(2); // user1 and user10
}

Common DAO Testing Patterns

Entity Lifecycle Testing

@Test
public void testEntityLifecycle() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    // Create
    User user = daoTest.inTransaction(() -> {
        User newUser = new User("Dave", "dave@example.com");
        return userDAO.save(newUser);
    });
    assertThat(user.getId()).isNotNull();
    
    // Read
    Optional<User> foundUser = daoTest.inTransaction(() -> 
        userDAO.findById(user.getId())
    );
    assertThat(foundUser).isPresent();
    
    // Update
    daoTest.inTransaction(() -> {
        User toUpdate = userDAO.findById(user.getId()).orElseThrow();
        toUpdate.setEmail("dave.updated@example.com");
        userDAO.save(toUpdate);
    });
    
    User updatedUser = daoTest.inTransaction(() -> 
        userDAO.findById(user.getId()).orElseThrow()
    );
    assertThat(updatedUser.getEmail()).isEqualTo("dave.updated@example.com");
    
    // Delete
    daoTest.inTransaction(() -> {
        userDAO.delete(user.getId());
    });
    
    Optional<User> deletedUser = daoTest.inTransaction(() -> 
        userDAO.findById(user.getId())
    );
    assertThat(deletedUser).isEmpty();
}

Query Performance Testing

@Test
public void testQueryPerformance() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    // Create large dataset
    daoTest.inTransaction(() -> {
        for (int i = 0; i < 1000; i++) {
            User user = new User("User" + i, "user" + i + "@example.com");
            userDAO.save(user);
        }
    });
    
    // Test query performance
    long startTime = System.currentTimeMillis();
    
    List<User> results = daoTest.inTransaction(() -> 
        userDAO.findActiveUsersByDomain("example.com")
    );
    
    long endTime = System.currentTimeMillis();
    long duration = endTime - startTime;
    
    assertThat(results).isNotEmpty();
    assertThat(duration).isLessThan(1000); // Should complete within 1 second
}

Constraint Violation Testing

@Test
public void testUniqueConstraintViolation() {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    assertThatThrownBy(() -> {
        daoTest.inTransaction(() -> {
            // Create first user
            User user1 = new User("Alice", "alice@example.com");
            userDAO.save(user1);
            
            // Try to create second user with same email (should violate unique constraint)
            User user2 = new User("Alice2", "alice@example.com");
            userDAO.save(user2);
        });
    }).hasCauseInstanceOf(ConstraintViolationException.class);
}

Transaction Isolation Testing

@Test
public void testTransactionIsolation() throws Exception {
    UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());
    
    // Create initial data
    User user = daoTest.inTransaction(() -> {
        User newUser = new User("Eve", "eve@example.com");
        return userDAO.save(newUser);
    });
    
    // Simulate concurrent modification
    CountDownLatch latch = new CountDownLatch(2);
    AtomicReference<Exception> exception = new AtomicReference<>();
    
    // Transaction 1: Update user
    CompletableFuture.runAsync(() -> {
        try {
            daoTest.inTransaction(() -> {
                User toUpdate = userDAO.findById(user.getId()).orElseThrow();
                toUpdate.setName("Eve Updated");
                Thread.sleep(100); // Simulate processing time
                userDAO.save(toUpdate);
            });
        } catch (Exception e) {
            exception.set(e);
        } finally {
            latch.countDown();
        }
    });
    
    // Transaction 2: Concurrent read
    CompletableFuture.runAsync(() -> {
        try {
            User readUser = daoTest.inTransaction(() -> 
                userDAO.findById(user.getId()).orElseThrow()
            );
            // Should read original value due to isolation
            assertThat(readUser.getName()).isEqualTo("Eve");
        } catch (Exception e) {
            exception.set(e);
        } finally {
            latch.countDown();
        }
    });
    
    latch.await(5, TimeUnit.SECONDS);
    
    if (exception.get() != null) {
        throw exception.get();
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-dropwizard--dropwizard-testing

docs

configuration-overrides.md

core-test-support.md

dao-testing.md

index.md

junit5-extensions.md

resource-testing.md

tile.json