Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow certain features and extensions to be turned off while testing #23175

Open
lgtti opened this issue Jan 25, 2022 · 14 comments
Open

Allow certain features and extensions to be turned off while testing #23175

lgtti opened this issue Jan 25, 2022 · 14 comments
Labels
area/testing kind/enhancement New feature or request

Comments

@lgtti
Copy link

lgtti commented Jan 25, 2022

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

@lgtti lgtti added the kind/bug Something isn't working label Jan 25, 2022
@geoand geoand added area/testing kind/question Further information is requested and removed triage/needs-triage labels Jan 25, 2022
@famod
Copy link
Member

famod commented Jan 25, 2022

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?

@lgtti
Copy link
Author

lgtti commented Jan 25, 2022

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?

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...

@famod
Copy link
Member

famod commented Jan 25, 2022

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?

@famod
Copy link
Member

famod commented Jan 25, 2022

@geoand @stuartwdouglas maybe it's time to revist #12512.

@lgtti
Copy link
Author

lgtti commented Jan 26, 2022

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?

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.

@rkraneis
Copy link

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.

@lgtti
Copy link
Author

lgtti commented Jan 26, 2022

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).

@stuartwdouglas
Copy link
Member

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.

@lgtti
Copy link
Author

lgtti commented Jan 27, 2022

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.

@geoand
Copy link
Contributor

geoand commented Jan 27, 2022

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

@famod famod changed the title Unit Testing in a multi-module environment Allow certain features and extensions to be turned off while testing Feb 6, 2022
@famod famod added kind/enhancement New feature or request and removed kind/bug Something isn't working kind/question Further information is requested labels Feb 6, 2022
@famod
Copy link
Member

famod commented Feb 6, 2022

FTR: I rescoped this issue to be in line with the latest state of the discussion.

@voronovmaksim
Copy link

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

public class ArcTestContainer implements BeforeEachCallback, AfterEachCallback {
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.

@geoand
Copy link
Contributor

geoand commented Apr 26, 2022

@voronovmaksim can you please expand a little on your use case?

It sounds somewhat similar to what I had proposed in #14927 (comment)

@voronovmaksim
Copy link

voronovmaksim commented Apr 26, 2022

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 #14927 (comment)

Your proposal #14927 (comment) fits to me.

Actually I would like run quarkus test only with specified extension rather than with all extensions that exist in classpath.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/testing kind/enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants