4
votes

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.

2
The @Mock annotation is used for automatically initializing mocks (either by a suitable testrunner, or calling Mockito.initMocks(this)). You don't need @Mock and Mockito.mock(), you need just one of them.Kayaman
@Kayaman OK, without it I'm unable to do a Mockito.When(myClass.Method()) as myClass is null? And the mock isn't injected into the constructor, it's my actual implementation that is.cstdev
Don't inject it if you're trying to mock it. Or mock it separately for a test if you only want to use mock for a single test.Kayaman
@Kayaman Maybe it's more of a REST-assured question than anything, I need to mock the service class that the endpoint it calls uses. I've updated the question to show what I mean, hopefully. And perhaps it's not the right tool for the job/method to do this. I could construct the Controller and pass in my mock, it's how I'd normally do it. Was just trying to use Rest assured.cstdev
If you end up calling the real instances, then your mocks aren't where you think they are. Your example code seems to mock InputPart and tableExtractorServiceMock, but maybe you're not injecting them to the places they're used?Kayaman

2 Answers

0
votes

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/