mocking at several levels

148 views
Skip to first unread message

vvs...@gmail.com

unread,
Oct 23, 2020, 12:11:20 PM10/23/20
to Quarkus Development mailing list
hello,

I was playing with the capabilities provided by QuarkusMock.

I created the following class with some inspiration from https://quarkus.io/guides/getting-started-testing#mocking-using-quarkusmock:

@QuarkusTest
public class MockTestCase {

@Inject
Bean1 bean1;

@Inject
Bean2 bean2;

@Test
public void testNormal() {
Assertions.assertEquals("m1-m2", bean1.m1());
}

@Test
public void testPerTestMock() {
QuarkusMock.installMockForInstance(new MockBean1(), bean1);
QuarkusMock.installMockForInstance(new MockBean2(), bean2);

Assertions.assertEquals("Xm1-Ym2", bean1.m1());
}

@ApplicationScoped
public static class Bean2 {
public String m2() {
return "m2";
}
}

@ApplicationScoped
public static class Bean1 {

@Inject
Bean2 bean2;

public String m1() {
return "m1-" + bean2.m2();
}
}

public static class MockBean1 extends Bean1 {
@Override
public String m1() {
return "Xm1-" + bean2.m2();
}
}

public static class MockBean2 extends Bean2 {
@Override
public String m2() {
return "Ym2";
}
}
}
my hope was that, although I instantiate directly MockBean1, it would still benefit from the injection that is on its superclass, so MockBean1.bean2 would be an instance of a valid Bean2 (a MockBean2 in that case).

but MockBean1.bean1 is null and I get a NPE. I guess when you use installMockForInstance, you are on your own with the instance you create.

I thought about InjectSpy, but we very quickly reach the limits when the overriding behavior is more than just returning a canned response.

on one hand we have @Mock, which does not allow limiting a mock to a single test.
and on the other hand, we have installMockForInstance, which does not allow to benefit from injection in the superclass.

it would be nice to have:
QuarkusMock.installMockClass(MockBean1.class, Bean1.class);

any way to do this?
thanks.

V. Sevel

unread,
Oct 23, 2020, 4:07:06 PM10/23/20
to Quarkus Development mailing list
answering my own question...

the closest I got to what I was looking for was described here:

I adapted my example to be:

public class MyProfile implements QuarkusTestProfile {

@Override
public Set<Class<?>> getEnabledAlternatives() {
return Set.of(MockBean1.class, MockBean2.class, MockBean3.class);
}
}

@QuarkusTest
@TestProfile(MyProfile.class)
public class MockTestCase {

@Inject
Intf1 bean1;

@Inject
Bean3 bean3;

@Test
public void testPerTestMock() {
Assertions.assertEquals("Xm1-Ym2", bean1.m1());
Assertions.assertEquals("Zm3-Ym2", bean3.m3());
}
}

the only drawback was that I had to separate the normal test (i.e. not mocked) and the mocked test (above), because the TestProfile is expressed at the type level only.

the advantage, apart from providing injection inside mock base classes, is that it works for @Singleton beans, not just @ApplicationScoped and @Request as discussed in https://quarkus.io/guides/getting-started-testing#mocking-using-quarkusmock

it also relies on true cdi injection, as opposed to QuarkusMock.installMockForType(), which underneath only swaps out the bean delegate to be the mock. this means that if a new client proxy is created for the same bean (may be in the context of another injection point), it would still point to the original delegate, not the mock.

one idea for improvement: it is a bit cumbersome to create a class just to specify a list of alternative classes. something like this could be nice:

@QuarkusTest
@MockAlternatives({MockBean1.class, MockBean2.class})
public class MockTestCase {
...

another idea would be to allow this at the method level. for instance:

@Test
@MockAlternatives({MockBean1.class, MockBean2.class})
public void test12() {
...
}

@Test
@MockAlternatives({MockBean3.class})
public void test3() {
...
}

this would be useful with maybe the inconvenient to encourage quarkus context recreation as discussed in:

Georgios Andrianakis

unread,
Oct 26, 2020, 2:16:47 AM10/26/20
to vvs...@gmail.com, Quarkus Development mailing list
This is standard behavior of Mockito (upon which this feature relies). 

I thought about InjectSpy, but we very quickly reach the limits when the overriding behavior is more than just returning a canned response.

on one hand we have @Mock, which does not allow limiting a mock to a single test.
and on the other hand, we have installMockForInstance, which does not allow to benefit from injection in the superclass.

it would be nice to have:
QuarkusMock.installMockClass(MockBean1.class, Bean1.class);

any way to do this?
thanks.

So basically what you are asking for here is an easy way to set fields of a mock?

--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/quarkus-dev/CAK9Ue%2BZ6kEJw1SKejcK1vuqAybGvU4CQJ71dYCyPsJNJKmCKog%40mail.gmail.com.

Georgios Andrianakis

unread,
Oct 26, 2020, 2:19:05 AM10/26/20
to vvs...@gmail.com, Quarkus Development mailing list
This already exists in the form of QuarkusTestProfile#getEnabledAlternatives, right?
I really don't want to start adding testing annotations all over the place because I am afraid of ending up in a situation where we'll have a bunch of testing annotations that users won't remember and the combinations of which may or may not work.
--
You received this message because you are subscribed to the Google Groups "Quarkus Development mailing list" group.
To unsubscribe from this group and stop receiving emails from it, send an email to quarkus-dev...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages