Nedim
Nedim

Reputation: 349

Are files in src/test/resources on classpath while application (src/main) is running?

newbie here! I found a lot of similar questions on SO, but none of them gave me a strait answer.

I wrote tests for all layers including the persistence layer in my spring-boot application. For testing the persistence layer I used H2 database. I used @DataJpaTest annotation and placed schema.sql and data.sql scripts to src/test/resources/db folder to create a schema and populate it with data from data.sql. And it works fine, all tests passing using that data.

Now what I am trying to do is to set up an integration test with @SpringBootTest to test whole application - top to bottom, from web layer to data layer. I made an application-test.properties with H2 config and annotated my test class with @ActiveProfiles("test") and it works fine. When I run this test class, application context fires up, H2 is up too, but of course, there is no data there. I would like to populate my in-memory database with the same date from data.sql from src/test/resources/db.

I modified application-test.properties in this way: spring.datasource.url=jdbc:h2:mem:testdb;INIT=RUNSCRIPT FROM 'classpath:db/schema.sql'\;RUNSCRIPT FROM 'classpath:db/data.sql' and I got org.h2.jdbc.JdbcSQLNonTransientException: IO Exception: "java.io.FileNotFoundException: resource /db/schema.sql" exception.

I have done a lot of googling and I found more resources saying src/main/resources and src/test/resources are both on the classpath. If so, why am I getting this exception?

Also, when I put those two scripts to src/main/resources/db it works fine. Also, it works fine when I run my persistence layer test no matter if those scripts are in src/main/resources/db or src/test/resources/db. So, it looks like a test environment using both src/main/resources and src/test/resources locations as classpath, but the environment uses only src/main/resources as classpath!? Could it be true?

One possible solution I tried and its works is not to run those scripts (removing INIT=RUNSCRIPT FROM 'classpath:db/schema.sql'\;RUNSCRIPT FROM 'classpath:db/data.sql' from .properties file) and before test programmatically create some object and persist them to database and then write test, but I think it is much easier and leaner to populate data from script than write this code in every test class.

Another possible solution is to copy those scripts from src/test/resources/db to src/main/resources/db, but it means I would have duplicated data and I hate that. Also, it means harder to maintaince.

The next possible solution is to put those scripts to src/main/resources/db because, as I said, data persistence test works fine no matter if those scripts are in src/main/resources/db or src/test/resources/db. But, those scripts are related to tests so I think they belong there not in the main part of the application.

And also, if I provide an absolute path to those scripts, it works fine, but it means my team members would have to modify it to fit their machines. Not really optimal solution.

So, my questions are: 1. Is it true that the test environment using both src/main/resources and src/test/resources locations as classpath, but the main environment uses only src/main/resources as classpath? 2. How can I resolve this challenge?

Upvotes: 4

Views: 5271

Answers (1)

madhead
madhead

Reputation: 33441

Is it true that the test environment using both src/main/resources and src/test/resources locations as classpath, but the main environment uses only src/main/resources as classpath?

Yes. It is true. In both Maven and Gradle test classpath is inherited from main and not vice versa. It means that classes and resources from src/main/java and src/main/resources are available during test execution, but src/test/java and src/test/resources are not reachable by the main codebase. Also note that test classes and resources are not transitive, i.e. they are not available in a depended projects.

How can I resolve this challenge?

Well, there are many solutions:

  1. Place the resources in src/main (obviously)
  2. Extract the resources you need in both places into a separate module and depend on it.
  3. Use maven-resources-plugin to copy the files from other locations.

Upvotes: 6

Related Questions