Search code examples
testingjunitmockingsingletonquarkus

Why can't i change the singleton scope to applicationscoped for a quarkus test?


I have the following class with scope singleton in Quarkus. I am trying to mock it for a quarkus test:

   @Path("api/rpo/registerdata/public/{filename}")
@RegisterRestClient(configKey = "gpo.upload")
@ClientHeaderParam(name = "Authorization", value = "{pe.dev.rpo.gpo.JwtStore.getBearer}")
public interface RpoService {
    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    Response upload(InputStream inputStream, @PathParam("filename") String filename);
}

I define the scope in the application.propeties:

gpo.upload/mp-rest/scope=javax.inject.Singleton

When I inject it in the test with the following annotation:

  @InjectMock(convertScopes = true)
    @RestClient
    RpoService rpo;

I get an error : the injected bean does not declare a CDI normal scope but: javax.inject.Singleton

The docs of Quarkus mention that mocking a @Singleton class is not possible. But as a way around, @InjectMock(convertScopes = true) makes it possible to change the scope to @ApplicationScoped so it can be mocked. However, it is not working in my case. Could anybody tell me why and maybe help me with a solution? My test class:

@QuarkusTest
public class ExportServiceTest {
    @Inject
    ExportService exportService;

    @InjectMock(convertScopes = true)
    @RestClient
    RpoService ax;

    @Test
    void test_upload() throws IOException {

        File f = File.createTempFile( "some-prefix", "some-ext");
        Path p= f.toPath();
        FileInputStream inputStream = new FileInputStream(f);
        String fileName ="randomstring";
        Response mockResponse = mock(Response.class);
        when(ax.upload(inputStream, fileName)).thenReturn(mockResponse);
        when(mockResponse.getStatus()).thenReturn(201);
         exportService.uploadDelete(p,fileName);
        assertThat(ax.upload(inputStream, fileName).getStatus()).isEqualTo(201);
        f.deleteOnExit();

    }

Solution

  • This is because RestClient Reactive generates an implementation of the interface, called RpoService$$CDIWrapper in this case, and that is the actual bean that's used. The convertScopes mechanism is not aware of this -- it only works if a managed bean or a producer method exists whose bean class is RpoService. No such bean exists here.

    I suggest you file an issue in Quarkus.

    Alternatively, if you're fine with converting scopes for testing, maybe you can make the RestClient bean @ApplicationScoped in the first place.