Bird
Raised Fist0
JUnittesting~15 mins

In-memory database testing in JUnit - Build an Automation Script

Choose your learning style10 modes available
Verify user data insertion and retrieval using in-memory H2 database
Preconditions (4)
Step 1: Start the in-memory H2 database
Step 2: Create a User object with id=1, name='Alice', email='alice@example.com'
Step 3: Save the User object to the database using UserRepository.save()
Step 4: Retrieve the User object by id=1 using UserRepository.findById()
Step 5: Verify that the retrieved User is not null
Step 6: Verify that the retrieved User's name is 'Alice'
Step 7: Verify that the retrieved User's email is 'alice@example.com'
✅ Expected Result: The User is saved and retrieved correctly from the in-memory database with matching id, name, and email
Automation Requirements - JUnit 5 with H2 in-memory database
Assertions Needed:
assertNotNull for retrieved User
assertEquals for User name
assertEquals for User email
Best Practices:
Use @BeforeEach to set up database connection
Use @AfterEach to clean up resources
Use explicit assertions with clear messages
Use try-with-resources or proper resource management
Keep test isolated and repeatable
Automated Solution
JUnit
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.*;
import java.sql.*;

class User {
    private int id;
    private String name;
    private String email;

    public User(int id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public int getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
}

class UserRepository {
    private Connection connection;

    public UserRepository(Connection connection) {
        this.connection = connection;
    }

    public void save(User user) throws SQLException {
        String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setInt(1, user.getId());
            stmt.setString(2, user.getName());
            stmt.setString(3, user.getEmail());
            stmt.executeUpdate();
        }
    }

    public User findById(int id) throws SQLException {
        String sql = "SELECT id, name, email FROM users WHERE id = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setInt(1, id);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return new User(rs.getInt("id"), rs.getString("name"), rs.getString("email"));
                } else {
                    return null;
                }
            }
        }
    }
}

public class InMemoryDatabaseTest {
    private Connection connection;
    private UserRepository userRepository;

    @BeforeEach
    void setUp() throws SQLException {
        connection = DriverManager.getConnection("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1");
        try (Statement stmt = connection.createStatement()) {
            stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255), email VARCHAR(255))");
        }
        userRepository = new UserRepository(connection);
    }

    @AfterEach
    void tearDown() throws SQLException {
        try (Statement stmt = connection.createStatement()) {
            stmt.execute("DROP TABLE users");
        }
        connection.close();
    }

    @Test
    void testSaveAndFindUser() throws SQLException {
        User user = new User(1, "Alice", "alice@example.com");
        userRepository.save(user);

        User retrieved = userRepository.findById(1);

        assertNotNull(retrieved, "Retrieved user should not be null");
        assertEquals("Alice", retrieved.getName(), "User name should match");
        assertEquals("alice@example.com", retrieved.getEmail(), "User email should match");
    }
}

The test class InMemoryDatabaseTest uses JUnit 5 to test database operations on an in-memory H2 database.

Setup: The @BeforeEach method creates a fresh in-memory database and a users table before each test. This ensures tests are isolated and repeatable.

Teardown: The @AfterEach method drops the table and closes the connection to clean up resources.

Test: The testSaveAndFindUser method saves a User object and then retrieves it by ID. Assertions check that the retrieved user is not null and that the name and email match the expected values.

This approach uses explicit SQL and JDBC to keep the example simple and clear for beginners. It follows best practices like resource management with try-with-resources and clear assertion messages.

Common Mistakes - 4 Pitfalls
Not creating the database table before running tests
Not closing database connections or statements
Hardcoding data without cleanup, causing tests to interfere
Using assertEquals without checking for null first
Bonus Challenge

Now add data-driven testing with 3 different User inputs to verify save and retrieval

Show Hint

Practice

(1/5)
1. What is the main advantage of using an in-memory database for JUnit testing?
easy
A. It stores data permanently for production use
B. It requires complex setup and external servers
C. Tests run faster without needing a real database connection
D. It only works with web applications

Solution

  1. Step 1: Understand in-memory database purpose

    In-memory databases keep data in RAM, so no disk access is needed, making tests faster.
  2. Step 2: Compare options

    The incorrect options describe permanent storage, complex setup, and limitation to web apps, which are not advantages of in-memory testing databases.
  3. Final Answer:

    Tests run faster without needing a real database connection -> Option C
  4. Quick Check:

    In-memory = fast tests [OK]
Hint: In-memory means fast, no real DB needed [OK]
Common Mistakes:
  • Thinking in-memory DB stores data permanently
  • Assuming it needs external servers
  • Confusing test DB with production DB
2. Which annotation is commonly used in JUnit to configure a test with an in-memory database and JdbcTemplate?
easy
A. @DataJpaTest
B. @JdbcTest
C. @Entity
D. @SpringBootApplication

Solution

  1. Step 1: Identify annotation for JDBC testing

    @JdbcTest sets up an in-memory database and JdbcTemplate for simple JDBC tests.
  2. Step 2: Review other annotations

    @SpringBootApplication is for main app config, @Entity marks entities, @DataJpaTest is for JPA tests, not simple JDBC.
  3. Final Answer:

    @JdbcTest -> Option B
  4. Quick Check:

    @JdbcTest = in-memory JDBC test setup [OK]
Hint: Use @JdbcTest for fast in-memory JDBC tests [OK]
Common Mistakes:
  • Confusing @JdbcTest with @DataJpaTest
  • Using @Entity as a test annotation
  • Using @SpringBootApplication in tests
3. Given this JUnit test snippet using JdbcTemplate with an in-memory database:
jdbcTemplate.execute("CREATE TABLE users(id INT PRIMARY KEY, name VARCHAR(50))");
jdbcTemplate.update("INSERT INTO users VALUES (1, 'Alice')");
int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", Integer.class);

What is the value of count after execution?
medium
A. 1
B. 0
C. Syntax error
D. NullPointerException

Solution

  1. Step 1: Analyze table creation and insertion

    The table 'users' is created and one row with id=1 and name='Alice' is inserted.
  2. Step 2: Query the count of rows

    The query "SELECT COUNT(*) FROM users" returns the number of rows, which is 1 after insertion.
  3. Final Answer:

    1 -> Option A
  4. Quick Check:

    Inserted 1 row, count = 1 [OK]
Hint: Count rows after insert to get 1 [OK]
Common Mistakes:
  • Assuming count is 0 before insertion
  • Expecting syntax error from correct SQL
  • Confusing queryForObject return type
4. In this JUnit test using JdbcTemplate, what is the error?
@JdbcTest
public class UserTest {
  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Test
  public void testInsert() {
    jdbcTemplate.execute("CREATE TABLE users(id INT PRIMARY KEY, name VARCHAR(50))");
    jdbcTemplate.update("INSERT INTO users VALUES (1, 'Bob')");
    int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM user", Integer.class);
    assertEquals(1, count);
  }
}
medium
A. Table name typo in SELECT query
B. Missing @Test annotation
C. JdbcTemplate not autowired
D. Incorrect assertion syntax

Solution

  1. Step 1: Check table creation and query

    The table created is named 'users' but the SELECT query uses 'user', causing an error.
  2. Step 2: Verify annotations and syntax

    @Test is present, JdbcTemplate is autowired, and assertion syntax is correct.
  3. Final Answer:

    Table name typo in SELECT query -> Option A
  4. Quick Check:

    Table 'users' vs query 'user' mismatch [OK]
Hint: Match table names exactly in queries [OK]
Common Mistakes:
  • Ignoring table name mismatch
  • Thinking @Test is missing
  • Assuming autowiring fails silently
5. You want to write a JUnit test using an in-memory database that creates a products table, inserts multiple rows, and verifies the total price sum. Which approach is best to keep tests independent and reliable?
hard
A. Create table once in a static block and reuse data across tests
B. Skip table creation and assume it exists
C. Use a real database server for accurate results
D. Create table and insert data inside each test method

Solution

  1. Step 1: Understand test independence

    Creating tables and inserting data inside each test ensures no leftover data affects other tests.
  2. Step 2: Evaluate other options

    Static blocks or real DBs reduce isolation and speed; skipping creation risks failures if table missing.
  3. Final Answer:

    Create table and insert data inside each test method -> Option D
  4. Quick Check:

    Setup per test = independent, reliable [OK]
Hint: Setup fresh data inside each test method [OK]
Common Mistakes:
  • Sharing data across tests causing flaky results
  • Using real DB slowing tests
  • Assuming tables exist without creation
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard