Allow certain features and extensions to be turned off while testing
I want to add some unit tests to my Quarkus maven project. I'm working on a multi-module project structured as:
- parent
-
- persistence (2 profiles [postgres, oracle])
-
- business (depends on persistence module)
-
- resource (depends on business module)
-
- deploy-rest (with quarkus maven plugin)
-
- deploy-lambda (with quarkus maven plugin)
The test is very simple (empty implementation) and is located in the business module.
@QuarkusTest
class CARootCreateControllerImplTest {
@Test
void handleRequest() {
}
}
When I try to execute my test, quarkus raises an error because agroal (from persistence dependency) needs the database configuration. First question: how can I run only unit tests (not integration) that don't use the database but only mocks?
However, I have tried to solve the problem by adding a test/resource/application.properties fake configuration and agroal this time found it, raising another error. This second error is that the Postgres-JDBC driver is not found. This is correct because only the persistence module knows the dependency (using the correct profile). Second question: if it is not possible to avoid the first question, how can I avoid placing additional dependencies on the module that runs the test?
Expected behavior
I want to run a single unit test (not integration) without database configuration/installation in Quarkus.
Actual behavior
If a single Entity is defined, agroal extension need a configuration to a running database
How to Reproduce?
Add a test like
@QuarkusTest
class CARootCreateControllerImplTest {
@Test
void handleRequest() {
}
}
in a maven module with the agroal extension enabled and no application.properties configured.
Output of uname -a or ver
Linux 2019-150507 5.10.60.1-microsoft-standard-WSL2
Output of java -version
OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode)
GraalVM version (if different from Java)
No response
Quarkus version or git rev
2.2.3-Final
Build tool (ie. output of mvnw --version or gradlew --version)
Apache Maven 3.6.3
Additional information
No response
I have no real solution for you, but this how we do it:
We have a core module which looks a bit like your persistence module but in our case it's also home to all "core services", so it's more a merge of your persistence and business modules.
All other modules depend on core.
We have all the respective Quarkus dependencies we need for persistence DB etc. in core and also the config for it!
All other modules also have config (in main!), but just the context-specific stuff, e.g. a module that uses the ReactiveMailer will only add mailer config.
So with this setup we have persistence in all tests, but e.g. in api (probably similar to your resource) we mock out the core services to concentrate on the REST part with OpenAPI. (The core services are covered in core.)
This is also why we have h2 database in tests because it's fast to boot and has no "dependencies" (separate DB machine/vm/container, testcontainer or so on). We do pay a price for this though, as migrations via Liquibase can look very different for h2 that MariaDB (our production database).
What you seem to be looking for sounds a bit like Spring test slices?
I have no real solution for you, but this how we do it:
We have a
coremodule which looks a bit like yourpersistencemodule but in our case it's also home to all "core services", so it's more a merge of yourpersistenceandbusinessmodules. All other modules depend oncore. We have all the respective Quarkus dependencies we need for persistence DB etc. incoreand also the config for it! All other modules also have config (inmain!), but just the context-specific stuff, e.g. a module that uses theReactiveMailerwill only add mailer config.So with this setup we have persistence in all tests, but e.g. in
api(probably similar to yourresource) we mock out the core services to concentrate on the REST part with OpenAPI. (The core services are covered incore.) This is also why we have h2 database in tests because it's fast to boot and has no "dependencies" (separate DB machine/vm/container, testcontainer or so on). We do pay a price for this though, as migrations via Liquibase can look very different for h2 that MariaDB (our production database).What you seem to be looking for sounds a bit like Spring test slices?
Thanks for the response! Your structure is indeed similar to mine and your advice is very useful. My test now works correctly with this configuration (may be useful for others):
persistence/pom.xml
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-h2</artifactId>
<scope>test</scope>
</dependency>
persistence/.../resources/application.properties
%test.quarkus.datasource.username=username
%test.quarkus.datasource.password=password
%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test
@QuarkusTest
@QuarkusTestResource(H2DatabaseTestResource.class)
class CARootCreateControllerImplTest {
@Test
void handleRequest() {
}
}
My only doubt is the procedure: why do I need a database set up to run unit tests? Quarkus solution is more similar to an integration test...
My only doubt is the procedure: why do I need a database set up to run unit tests? Quarkus solution is more similar to an integration test...
You have a point there. IIRC, being able to kinda "exclude" features (similar to test slices) was asked for a while ago. The advantage of the current solution (especially with Dev Services) is to quickly get to a point where you can test rather close to your actual app.
What you could do if you really want plain unit tests is to omit @QuarkusTest and separate the test types via e.g. @Tag("...") (and surefire config). But I suppose you do want some Quarkus features in those tests, e.g. injection?
@geoand @stuartwdouglas maybe it's time to revist #12512.
My only doubt is the procedure: why do I need a database set up to run unit tests? Quarkus solution is more similar to an integration test...
You have a point there. IIRC, being able to kinda "exclude" features (similar to test slices) was asked for a while ago. The advantage of the current solution (especially with Dev Services) is to quickly get to a point where you can test rather close to your actual app.
What you could do if you really want plain unit tests is to omit
@QuarkusTestand separate the test types via e.g.@Tag("...")(and surefire config). But I suppose you do want some Quarkus features in those tests, e.g. injection?
You are correct, I have tried to test using bare junit5 solution but how can I inject beans in my services? Don't misunderstand me, I'm not a purist of unit testing but my work pipeline doesn't permit running a docker container in the build phase (to set up a database, for example). So, I need to build and run unit tests without any other runtime dependency
The workaround using the h2 database works even because my code architecture extracts business logic from the DAO layer to the Repository layer, so I don't need to create tables in the h2 DB. DAO layer will be tested in the next pipeline layer.
However the problem still remains because more tests will be added, more slow became the build.
If you want to use CDI in unit tests without @QuarkusTest, you are on your own ;-). The ideas in https://dzone.com/articles/writing-tests-with-junit-5-and-cdi-20 may help, though. One more thing to be aware of: you will not see any log output in tests that are not mananged by Quarkus. This should be resolved in 2.7.0.
I don't want to reinvent the wheel. I only think that quarkus should split integration tests and unit tests, otherwise, a note in the official documentation should be added indicating that quarkus doesn't support classic unit tests. This is a very huge technical limitation, in my opinion, because not all build pipelines support running temp Docker containers (as in my case).
The big question with this is what is considered unit vs what is considered integration, e.g. some people will still want the database connection but not the HTTP server, while others will only want CDI. Basically for every extension we would need to go through and make an arbitrary decision on what is actually started in unit tests, and it will never really be 100% right.
Something we could potentially do is allow certain features and extensions to be turned off.
Yes, I agree with this concept. As I said earlier, I'm not a purist or Taleban of unit testing. If anyone wants to use a DB in unit testing it's ok for me. No problem. But I want to use my concept, a unit test without a database. It isn't a wrong concept. Questionable yes but not wrong. I try to recap the situation simulating a new project for an enterprise factory:
Assumption: I want to add unit testing to my code. Great! To add unit testing to quarkus, at least one of these sentences is needed:
- An automatic CI/CD pipeline with the capability to run Docker during the build phase
- A non-docker database reached by the automatic CD/CD pipeline during the build phase
- An in-memory database that slows down the build
If you think of this scenario, who wants to add unit testing without a database will be penalized. This is the discussion point that I want to be clear about.
So, it is ok for quarkus to provide a method to run unit test with a database easily, but i think that must be a method to run unit tests without a database.
The big question with this is what is considered unit vs what is considered integration, e.g. some people will still want the database connection but not the HTTP server, while others will only want CDI. Basically for every extension we would need to go through and make an arbitrary decision on what is actually started in unit tests, and it will never really be 100% right.
I totally agree with this and IMHO we should not be trying to make such guesses.
Something we could potentially do is allow certain features and extensions to be turned off.
Yeah, that sounds reasonable
FTR: I rescoped this issue to be in line with the latest state of the discussion.
Hi. We are forced to omit @QuarkusTest, @QuarkusUnitTest because of this.
We extend third-party application, create new extension based on extensions of the application and it uses quarkus features(arc container, security etc). So our modules have dependency on the third-party extensions, all beans of the extensions are loaded during our Unit test and so fail. All tests of the third-party application are integration, but our extension uses only unit test(It is historically formed), we have own test framework for it. It is very complicated to replace with integration test now, so we are forced used only unit test.
In order to resolve CDI issues, we use https://github.com/quarkusio/quarkus/blob/5cd42e46e17be26ea21a16631bf3075588c0b12a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java#L51 That's terrible, but we didn't find how to use Arc container without @QuarkusTest or @QuarkusUnitTest.
Serious problem is raised during unit tests of authorization. We have own authorization for quarkus application. We should use @TestSecurity for unit test, but the annotation doesn't work without @QuarkusTest. It is not clear how we can test own authorization for quarkus without @QuarkusTest (and so without quarkus application).
When spring was used, we just used @RunWith(SpringJUnit4ClassRunner.class), @WebAppConfiguration @Import and spring app with specific beans is loaded.
@voronovmaksim can you please expand a little on your use case?
It sounds somewhat similar to what I had proposed in https://github.com/quarkusio/quarkus/issues/14927#issuecomment-1061830090
Thanks a lot @geoand.
My use case is like use case of issue specified by you. I run unit test with annotation @QuarkusTest and the unit test is failed because of hibernate Exception, but my test case is just.
@QuarkusTest
public class MyUnitTest{
@Test
public void test() {
Assertions.assertEquals("hello Quarkus", "hello Quarkus");
}
}
Hibernate is not needed in unit test, but it is required in my extension. So it is my problem https://github.com/quarkusio/quarkus/issues/14927#issuecomment-1087300805
Your proposal https://github.com/quarkusio/quarkus/issues/14927#issuecomment-1061830090 fits to me.
Actually I would like run quarkus test only with specified extension rather than with all extensions that exist in classpath.
Hi everyone, has there been any progress regarding this issue? I tried digging for some solutions and hacks, but found nothing. Came around this problem recently and IMHO with this feature test would've been able to run a lot faster. In my situation I was using oidc keycloak extension and to test some unrelated to security things I had to include quarkus-test-oidc-server which provides a WireMock instance just to be able to run test that isn't even using it.
TL;DR Can u guys explain what are the complexities behind a possible solution to this feature request? Are there any impossibilities to exclude dependencies at the part of cdi or it simply isn't in a scope of quarkus ideology? Thx in advance.
We can remove extensions during the build process but off the top of my head I can imagine that it would lead to hard to understand error messages if user code uses stuff from those extensions.