cstdev
cstdev

Reputation: 61

How to inject mock for only one test case with Quarkus/RestAssured

I'm attempting to test a REST controller (using Quarkus) endpoint using rest assured. I want to mock a class that is injected into that controller (ideally with Mockio), but only for one of my tests. Or get different behaviour per test case without having to have separate classes for each test. I'm not sure how to do this?

I've seen doing it the way from the documentation:

@Mock
@ApplicationScoped 
public class MockExternalService extends ExternalService {

    @Override
    public String service() {
        return "mock";
    }
}

But this would only allow me to use one mock for all tests and not mock certain behaviours based on tests as I would with Mockito. I think?

I've tried creating a mock and annotating it with @Mock

    @Mock
    public TableExtractorService tableExtractorServiceMock = Mockito.mock(TableExtractorService.class);;

but I still get my real implementation when I use it. I'm using a constructor annotated with @Inject in my Controller that takes the TableExtractorService.

For a bit more information my test using restassured looks like this:

InputPart filePart = Mockito.mock(InputPart.class);
Mockito.when(tableExtractorServiceMock.Extract(anyObject()))
       .thenThrow(IOException.class);
        
final InputStream inputStream = filePart.getBody(InputStream.class, null);

given()
                .multiPart("file", inputStream)
                .when().post("/document")
                .then()
                .statusCode(500);

That endpoint calls the service class that I'm trying to mock, and I want that mock to return an exception.

Upvotes: 6

Views: 15353

Answers (3)

Holly Cummins
Holly Cummins

Reputation: 11482

As @Ankush said, a class annotated with the @Mock annotation is using the CDI @Alternative mechanism, and will be global. @QuarkusTestProfiles can be used to define CDI @Alternatives for groups of tests.

For example, instead of annotating the mock with @Mock, it could be referenced in a test profile as

    default Set<Class<?>> getEnabledAlternatives() {
        return Set.of(MyMockThing.class);
    }

Any test annotatated with the

@TestProfile(MyMockyTestProfile.class)

profile would get those mocks, while others would use the original implementation.

What may be a simpler method is to just use @InjectMock. For example, in the test class, declaring a field like this:

    @InjectMock
    MyThing mock;

will ensure that mock is used by the classes under test, just for this test.

For rest clients, it will also be necessary to add a @RestClient annotation, and if the original implementation is a singleton, convertscopes can be used to coax the scopes into something mockable.

    @RestClient
    @InjectMock(convertScopes = true)
    MyThing mock;

Behaviour can be added to the injected mock in @BeforeEach or @BeforeAll methods. For example

    @BeforeEach
    public void setup() {
        when(mock.someMethod()).thenReturn("some value");
    }

Upvotes: 0

Ankush
Ankush

Reputation: 21

It can't be done. Quarkus documentation explains the issue:-

Although this mechanism is fairly straightforward to use, it nonetheless suffers from a few problems:

A new class (or a new CDI producer method) needs to be used for each bean type that requires a mock. In a large application where a lot of mocks are needed, the amount of boilerplate code increases unacceptably.

There is no way for a mock to be used for certain tests only. This is due to the fact that beans that are annotated with @Mock are normal CDI beans (and are therefore used throughout the application). Depending on what needs to be tested, this can be very problematic.

There is a no out of the box integration with Mockito, which is the de-facto standard for mocking in Java applications. Users can certainly use Mockito (most commonly by using a CDI producer method), but there is boilerplate code involved.

Link for reference: https://quarkus.io/blog/mocking/

Upvotes: 2

According Quarkus test documentation, you can do it usingo @QuarkusMock or @InjectMock.

Upvotes: 1

Related Questions