1

I created a sample application here if someone wants to replicate the issue - https://github.com/jainishan/spring-boot-tryouts/blob/main/src/test/java/com/samples/sample/RepositoryTestWithJDBC.java

The above test case is pretty much what I am trying to achieve.

My use case is simple - I need to truncate all tables in a generic way before the execution of each test case in my spring boot integration tests. I am using test containers in my setup and want to run a stored procedure to clear up all tables.

I wrote a stored procedure that runs fine in the DB but doesn't execute via @Sql annotation because of delimiter issues. I was not able to solve that problem - MYSQL procedure runs fine in DB but throwing errors while running within spring boot integration tests

Now I wrote a method to do it programatically using JDBC template - https://github.com/jainishan/spring-boot-tryouts/blob/main/src/test/java/com/samples/sample/config/BaseIntegrationTestJDBC.java#L24

I want to wire my integration tests in a way so that the method to clean up all tables run before the @Sql annotation. But the @Sql annotation always gets executed first and messes up the whole setup.

This sounds like a common requirement - to clean up tables right before each test case. I wonder how others are doing it ?

ishan
  • 1,202
  • 5
  • 24
  • 44
  • *This sounds like a common requirement - to clean up tables right before each test case. I wonder how others are doing it ?* - simple, via @SQL annotation – Antoniossss Jun 09 '23 at 16:00

1 Answers1

1

You can achieve that in the following way. First, create a JUnit extension, by implementing BeforeEachCallback, that would do the cleaning part:

public class DatabaseCleanerExtension implements BeforeEachCallback {
  private JdbcTemplate jdbcTemplate;

  @Override
  public void beforeEach(ExtensionContext extensionContext) {
    if (jdbcTemplate == null) {
      jdbcTemplate = SpringExtension.getApplicationContext(extensionContext).getBean(JdbcTemplate.class);
    }
    cleanDatabase();
  }

  private void cleanDatabase() {
    jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0");
    List<String> tableNames = jdbcTemplate.queryForList(
        "SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE()", String.class);
    for (String tableName : tableNames) {
      jdbcTemplate.execute("TRUNCATE TABLE " + tableName);
    }
    jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1");
  }
}

Since extensions are not managed by Spring, jdbcTemplate cannot be autowired directly. However, it can be retrieved from the extension context using the line below, as explained in Sam's answer.

SpringExtension.getApplicationContext(extensionContext).getBean(JdbcTemplate.class)

Then create a new class and annotate it with @ExtendsWith(DatabaseCleanerExtension.class).

@ExtendsWith(DatabaseCleanerExtension.class)
public class DatabaseCleaner {}

Finally, modify the BaseIntegrationTestJDBC class as shown below:

@ActiveProfiles("it")
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class BaseIntegrationTestJDBC extends DatabaseCleaner {}

The catch is to put the @ExtendsWith(DatabaseCleanerExtension.class) annotation on the top of the superclass of the class annotated with @SpringBootTest. Otherwise, @Sql would still be executed before the beforeEach method defined in the extension.

Toni
  • 3,296
  • 2
  • 13
  • 34
  • Thanks for taking the time to write this answer. Looks like, using the callback has exact same behaviour as annotation. Here is the code https://github.com/jainishan/spring-boot-tryouts/blob/main/src/test/java/com/samples/sample/RepositoryTestWithJDBCExtension.java – ishan Jun 13 '23 at 08:10
  • @ishan Okay, I've found the catch, I will update the answer. – Toni Jun 13 '23 at 16:54
  • Hi. It works fine this way. Although, it doesn't look good but it works. – ishan Jun 19 '23 at 12:56
  • I know, but at least it solves the problem. I haven't found a better solution or another solution that works yet. – Toni Jun 19 '23 at 13:15