zonkyio / embedded-database-spring-test

A library for creating isolated embedded databases for Spring-powered integration tests.
Apache License 2.0
408 stars 37 forks source link

Application context is getting initialized too early to let other configs take place #277

Open Mittal-Shivam opened 2 months ago

Mittal-Shivam commented 2 months ago

I am using zonky embedded-postgres db for my spring boot integration tests as I have SQL queries with arrow (->) operator which only work with a postgres database.

There are two issues I have observed:

  1. Setting a system property.

I used @BeforeAll to set a system property, say an API URL

    @BeforeAll
    static void init() {
        System.setProperty("service.url", "http://localhost:8080/api/v1/resource");
    }

This did not work with zonky's db, it only configured this property when declared in the application-test.properties. I tried the same thing with h2 as the embedded db, both ways, @BeforeAll method and test profile application-test.properties worked fine.

I could resolve it using a static block but I would like to know what makes h2 run it but not zonky.

Here is the sample code - embedded-pg.zip

  1. JPA repository within test case context unable to find data loaded from @Sql(script = "test-data.sql)

This issue occurred in one my organization's project, unfortunately I couldn't replicate it on a personal project. They are using spring-boot-parent-2.5.0 with Java 8.

In a spring boot integration test using @SpringBootTest and @AutoConfigureEmbeddedDatabase(type = AutoConfigureEmbeddedDatabase.DatabaseType.POSTGRES, provider = AutoConfigureEmbeddedDatabase.DatabaseProvider.ZONKY) , I loaded the data into db using @Sql script, verified the data load by enabling debug logs. My test case when trying to run a repository method was unable to find any data or schema for that matter from the query. Interestingly, an @Autowired repository object in the IT test class itself was able to access the db data.

For example, testQuery method would fetch data while getDeptTest would say "no such function exists". Note: This is not the behavior of attached code but it did happen on enterprise code with similar configs.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Sql("/test-schema.sql")
@ActiveProfiles("test")
@AutoConfigureEmbeddedDatabase(type = AutoConfigureEmbeddedDatabase.DatabaseType.POSTGRES, provider = AutoConfigureEmbeddedDatabase.DatabaseProvider.ZONKY)
public class DepartmentControllerIT {

    private static final String api = "http://localhost:%d/departments/get/1";
    private String url;

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private DepartmentRepository departmentRepository;

    @BeforeAll
    static void init() {
        System.setProperty("service.url", "someUrl");
    }

    @BeforeEach
    void setUp() {
        url = String.format(api, port);
    }

    @Test
    void testQuery() {
        assertThat(departmentRepository.getId()).isEqualTo("0123456789");
    }

    @Test
    void getDeptTest() {
        Department actual = restTemplate.getForObject(url, Department.class);
        System.out.println(actual);
        assertThat(actual).isNotNull();
    }
}

Both of the issues make me think if @AutoConfigureEmbeddedDatabase changes how spring test context loads:

Thanks for your help.

tomix26 commented 2 months ago

Setting a system property.

What exactly is this supposed to do? If it's meant to change a configuration property in DepartmentService, which is a singleton bean, I doubt this could ever work, whether with H2 or Postgres database. Please send an example with H2 database so I can take a closer look. Otherwise, the correct solution is to use @TestPropertySource or @DynamicPropertySource, not this.

JPA repository within test case context unable to find data

I'm not sure whether the problem should occur when loading data directly in the test class or when calling via rest template, but both methods work for me. However, I had to make an adjustment when asserting the loaded data and remove the zero at the beginning of the expected value. This is likely due to conversion from string to integer type.

Snímek obrazovky 2024-08-09 v 16 54 36
Mittal-Shivam commented 2 months ago

Hi @tomix26 ,

Setting a system property.

It is not supposed to change the config property but facilitate the property, I have to do this inside @BeforeAll method because I'm running MockWebServer which has a dynamic port and I need to append that port.

Something like this:

    @BeforeAll
    static void init() {
        System.setProperty("service.url", "http://localhost:" + mockServer.getPort() + "/api/v1/resource");
    }

Here is an example with h2 - embedded-pg (2).zip

JPA repository within test case context unable to find data

As I went back to take some screenshots of the error, I actually found out it was indeed due to different contexts. The repository was only able to grab the table mapped to its entity and the rest of the data (schemas and tables) was sitting in a different context, inaccessible to the repository. I fixed it by annotating the test class as @Transactional. Although, I don't need to do the same in my personal project and it seems to work fine even for non-entity table/function.

Here is the sample - embedded-pg (3).zip

Thanks

tomix26 commented 2 months ago

Setting a system property

The difference between versions with H2 and Embedded Postgres databases lies in the EmbeddedDatabaseTestExecutionListener, which evaluates and process the @AutoConfigureEmbeddedDatabase annotation. This listener causes the Spring context initialization to begin before the @BeforeAll method is called. Whereas in the case of the H2 database, there is no such listener, and the Spring context initialization occurs after the @BeforeAll method.

I have the following comments on this:

JPA repository within test case context unable to find data

All tests are passing normally, I don't see any @Transactional annotation anywhere. So I don't know what I should be looking into. I need a reproducer that will make it clear what's wrong.