10

Quarkus getting started unittest describes how to mock injected services. However when trying to apply this to an injected rest client this does not seem to work.

In my application the class attribute to be injected is defined like this

  @Inject
  @RestClient
  MyService myService;

In my test code I created a mock service like this:

@Alternative()
@Priority(1)
@ApplicationScoped
public class MockMyService extends MyService {

    @Override
    public MyObject myServicemethos() {
        return new MyObject();
    }
}

Please note that this service is not registered or annotated as a RestClient. Running my unittests like this gives the following error:

org.junit.jupiter.api.extension.TestInstantiationException: TestInstanceFactory [io.quarkus.test.junit.QuarkusTestExtension] failed to instantiate test class [...MyMediatorTest]: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
    [error]: Build step io.quarkus.arc.deployment.ArcAnnotationProcessor#build threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type ...MyService and qualifiers [@RestClient]
    - java member: ...MyMediator#myService
    - declared on CLASS bean [types=[java.lang.Object, ...MyMediator], qualifiers=[@Default, @Any], target=...MyMediator]

    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeTestInstanceFactory(ClassTestDescriptor.java:314)
  ...

I can probably overcome this by adding an additional service layer. But that feels like heading in the wrong direction.

How can I solve this.

Kind regards,

misl

misl
  • 301
  • 1
  • 3
  • 8
  • Future readers may want to check out this: https://stackoverflow.com/questions/55513502/how-to-create-a-jandex-index-in-quarkus-for-classes-in-a-external-module/55513723#55513723 and this : https://www.byteslounge.com/tutorials/java-ee-cdi-beans-deployed-in-external-library-web-inf-lib-jar-file – granadaCoder Oct 22 '19 at 12:31

5 Answers5

10

I just hit the same problem. There seems to have updates in the documentation and some corner cases that I faced, but google search sent me here first so I'll add my investigation results for future readers.

According to the documentation you already do not need creating mock class https://quarkus.io/guides/getting-started-testing#using-injectmock-with-restclient

but can use it like

Service class

@RegisterRestClient(configKey = "country-api")
@ApplicationScoped
public interface MyService

Service Usage

@Inject
@RestClient
MyService myService;

Mock it in the test like

@InjectMock
@RestClient 
MyService myService;

So far so good but following the documentation https://quarkus.io/guides/rest-client if you need configKey you will probably finish with

# Your configuration properties
country-api/mp-rest/url=https://restcountries.eu/rest #
!!country-api/mp-rest/scope=javax.inject.Singleton # /

in your config file. And then will hit these issues

Ability to use InjectMock on MicroProfile RestClient # 8622

Usage of InjectMock on MicroProfile RestClient # 9630

Error when trying to Test RestClient # 12585

that say: if you are using configKey with RegisterRestClient have to care TO NOT HAVE

country-api/mp-rest/scope=javax.inject.Singleton # 

in the config file which takes precedence before the @ApplicationScoped on the MyService interface

isilona
  • 101
  • 1
  • 6
5

You don't need another level of indirection.

You can simply do:

@Alternative()
@Priority(1)
@ApplicationScoped
@RestClient
public class MockMyService extends MyService {

    @Override
    public MyObject myServicemethos() {
        return new MyObject();
    }
}

Note that I added the @RestClient annotation.

Update

It's probably more intuitive to use @RegisterRestClient instead of @RestClient as mentioned in the comments and in the answer by @Tushar

Update 2

Quarkus also has builtin mock support for CDI beans, see https://quarkus.io/guides/getting-started-testing#further-simplification-with-injectmock and https://quarkus.io/blog/mocking/

geoand
  • 60,071
  • 24
  • 172
  • 190
  • 1
    counterintuitive: I would have expected the mock to require `@RegisterRestClient` like the class/interface it's mocking. `@RestClient` is used at injection point. Consider documenting. – jordanpg Aug 15 '19 at 15:03
  • @jordanpg would you like to propose a PR with your suggestion? – geoand Aug 20 '19 at 12:34
  • As tested on quarkus release 1.1.0.Final, use `@RegisterRestClient` instead on `@RestClient`. Looks like its corrected as mentioned by @josdanpg in comment. – Tushar Dec 24 '19 at 15:28
1

For quarkus release 1.1.0.Final (Latest as of writing this), use @RegisterRestClient

@Alternative()
@Priority(1)
@ApplicationScoped
@RegisterRestClient
public class MockMyService extends MyService {
    @Override
    public MyObject myServicemethos() {
        return new MyObject();
    }
}
Tushar
  • 263
  • 3
  • 11
1

Use @Mock annotation

import io.quarkus.test.Mock;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import javax.enterprise.context.ApplicationScoped;

@Mock
@ApplicationScoped
@RestClient
public class MockMyService extends MyService {

    @Override
    public MyObject myServicemethos() {
        return new MyObject();
    }
}
aiman24
  • 21
  • 1
0

From quarkus.io guide -

By default, the @InjectMock annotation can be used for any normal CDI scoped bean (e.g. @ApplicationScoped, @RequestScoped). Mocking @Singleton beans can be performed by setting the convertScopes property to true (such as @InjectMock(convertScopes = true). This will convert the @Singleton bean to an @ApplicationScoped bean for the test.

@InjectMock(convertScopes = true)
@RestClient
MyRestService service;

Worked for me!!

https://quarkus.io/guides/getting-started-testing

Derrick
  • 3,669
  • 5
  • 35
  • 50