You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomee.apache.org by Xavier Dury <ka...@hotmail.com> on 2017/04/05 10:22:29 UTC

ApplicationComposer and qualified mocks

Hi,


When using ApplicationComposer, I can declare mocks in my test classes like that:


@Mock

MyDependency myDependency;


@MockInjector
public Class<?> mockInjector() { return MockitoInjector.class; }

But that does not seem to work with (un@Named) qualified mocks:


@Mock

@MyQualifier

MyDependency myDependency;

Of course, I can still make a producer for the qualified mock like this:

@Produces
@MyQualifier
MyDependency myDependency() { return Mockito.mock(MyDependency.class); }

But I wanted to know if that was the only way to do it because, now, I have to programmatically create the mock instead of declaring it with @Mock.

Thanks,

Xavier


Re: ApplicationComposer and qualified mocks

Posted by Romain Manni-Bucau <rm...@gmail.com>.
It looks better than current impl but not yet handling stereotypes (it
would need to process annotated types or equivalent i think).

If you are happy with it I am too, we dont have a tons of users of that
feature so any progress is good and we don't need to do it all at once.
Just ensure we have at least a test to prevent regressions.


Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<https://blog-rmannibucau.rhcloud.com> | Old Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | JavaEE Factory
<https://javaeefactory-rmannibucau.rhcloud.com>

2017-04-06 9:24 GMT+02:00 Xavier Dury <ka...@hotmail.com>:

> Hi Romain,
>
> I made a gist and wanted your opinion before I submit a PR:
>
> https://gist.github.com/kalgon/ee1e00c0fecdd86afb01fe1a6891454a
>
> I don't use MockRegistry or MockitoInjector anymore (everything happens in
> the extension)
> and wanted to know if that was ok for you.
>
> > From: Xavier Dury <ka...@hotmail.com>
> >
> > OK, I'll try to provide a patch when I can find some time.
> >
> > > From: Romain Manni-Bucau <rm...@gmail.com>
> > >
> > > I think I get the use case. Do you want to try to patch
> openejb-mockito?
> > >
> > > 2017-04-05 13:28 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> > >
> > > > Hi Romain
> > > >
> > > > > From: Romain Manni-Bucau <rm...@gmail.com>
> > > > >
> > > > > Hi Xavier,
> > > > >
> > > > > 2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> > > > >
> > > > > > Hi,
> > > > > >
> > > > > > When using ApplicationComposer, I can declare mocks in my test
> classes
> > > > > > like that:
> > > > > >
> > > > > > @Mock
> > > > > > MyDependency myDependency;
> > > > > >
> > > > > > @MockInjector
> > > > > > public Class<?> mockInjector() { return MockitoInjector.class; }
> > > > > >
> > > > > > But that does not seem to work with (un@Named) qualified mocks:
> > > > >
> > > > > Hmm, MockitoInjector has no link with CDI, it is for EJB injections
> > > > (which
> > > > > are not using CDI/@Inject)
> > > > >
> > > > > > @Mock
> > > > > > @MyQualifier
> > > > > > MyDependency myDependency;
> > > > >
> > > > > This looks like a cdi injection but @Inject is missing
> > > >
> > > > No, this is no CDI injection, it is the declaration of the qualified
> mock
> > > > in my test class.
> > > > That field is set by MockitoAnnotations.initMocks() (through
> MockInjector).
> > > >
> > > > > > Of course, I can still make a producer for the qualified mock
> like this:
> > > > > >
> > > > > > @Produces
> > > > > > @MyQualifier
> > > > > > MyDependency myDependency() { return Mockito.mock(MyDependency.
> class);
> > > > }
> > > > > >
> > > > > > But I wanted to know if that was the only way to do it because,
> now, I
> > > > > > have to programmatically create the mock instead of declaring it
> with
> > > > @Mock.
> > > > >
> > > > > The openejb-mockito extension only supports @Default and @Any
> qualifiers
> > > > (
> > > > > https://github.com/apache/tomee/blob/master/utils/
> > > > openejb-mockito/src/main/java/org/apache/openejb/mockito/
> > > > MockitoExtension.java)
> > > > > but no real blocker to support another one. The only issue is to
> define
> > > > how
> > > > > to select an instance based on a qualifier then.
> > > > >
> > > > > Note: @Named is not a "real" qualifier so you actually used
> @Default I
> > > > think
> > > >
> > > > When MockRegistry is looping on all the fields of the TestInstance to
> > > > gather
> > > > the mocks created by MockitoAnnotations.initMocks(), it should also
> keep
> > > > all
> > > > qualifiers declared on the field.
> > > >
> > > > Of course, this is not trivial with stereotypes and qualifiers added
> at
> > > > runtime
> > > > through BeforeBeanDiscover.addQualifier() (but all annotations
> could be
> > > > kept and
> > > > filtered in the MockitoExtension which has access to the
> BeanManager).
> > > >
> > > > MockitoExtension would then need to register a bean for each mock
> with the
> > > > correct type AND qualifiers.
> > > >
> > > > Here is a full example of what I want to do:
> > > >
> > > > @RunWith(ApplicationComposer.class)
> > > > @Classes(cdi = true, innerClassesAsBean = true)
> > > > public class MyTest {
> > > >
> > > >     public static class MyBean {
> > > >
> > > >         @Inject
> > > >         @MyQualifier
> > > >         public MyInterface myInterface;
> > > >
> > > >         public void printMessage() {
> > > >             System.out.println(myInterface.getMessage());
> > > >         }
> > > >     }
> > > >
> > > >     public interface MyInterface {
> > > >         String getMessage();
> > > >     }
> > > >
> > > >     @Retention(RetentionPolicy.RUNTIME)
> > > >     @Qualifier
> > > >     public @interface MyQualifier {}
> > > >
> > > >     @Inject
> > > >     private MyBean myBean;
> > > >
> > > >     @Mock
> > > >     @MyQualifier
> > > >     private MyInterface myInterface;
> > > >
> > > >     @Test
> > > >     public void test() {
> > > >         Mockito.doReturn("Hello world!").when(myInterface).
> getMessage();
> > > >         myBean.printMessage();
> > > >         Mockito.verify(myInterface).getMessage();
> > > >     }
> > > >
> > > >     @MockInjector
> > > >     public Class<? extends FallbackPropertyInjector> mockInjector() {
> > > >         return MockitoInjector.class;
> > > >     }
> > > > }
> > > >
> > > > Currently, that does not work because MyBean.myInterface won't be
> resolved.
> > > >
> > > > My current workaround is:
> > > >
> > > > @RunWith(ApplicationComposer.class)
> > > > @Classes(cdi = true, innerClassesAsBean = true)
> > > > public class MyTest {
> > > >
> > > >     public static class MockProducer {
> > > >
> > > >         @Produces
> > > >         @MyQualifier
> > > >         static MyInterface myInterface = Mockito.mock(MyInterface.
> class);
> > > >     }
> > > >
> > > >     public static class MyBean {
> > > >
> > > >         @Inject
> > > >         @MyQualifier
> > > >         public MyInterface myInterface;
> > > >
> > > >         public void printMessage() {
> > > >             System.out.println(myInterface.getMessage());
> > > >         }
> > > >     }
> > > >
> > > >     public interface MyInterface {
> > > >         String getMessage();
> > > >     }
> > > >
> > > >     @Retention(RetentionPolicy.RUNTIME)
> > > >     @Qualifier
> > > >     public @interface MyQualifier {}
> > > >
> > > >     @Inject
> > > >     private MyBean myBean;
> > > >
> > > >     @Inject
> > > >     @MyQualifier
> > > >     private MyInterface myInterface;
> > > >
> > > >     @Test
> > > >     public void test() {
> > > >         Mockito.doReturn("Hello world!").when(myInterface).
> getMessage();
> > > >         myBean.printMessage();
> > > >         Mockito.verify(myInterface).getMessage();
> > > >     }
> > > > }
> > >
>

Re: ApplicationComposer and qualified mocks

Posted by Xavier Dury <ka...@hotmail.com>.
Hi Romain,

I made a gist and wanted your opinion before I submit a PR:

https://gist.github.com/kalgon/ee1e00c0fecdd86afb01fe1a6891454a

I don't use MockRegistry or MockitoInjector anymore (everything happens in the extension)
and wanted to know if that was ok for you.

> From: Xavier Dury <ka...@hotmail.com>
>     
> OK, I'll try to provide a patch when I can find some time.
> 
> > From: Romain Manni-Bucau <rm...@gmail.com>
> >     
> > I think I get the use case. Do you want to try to patch openejb-mockito?
> > 
> > 2017-04-05 13:28 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> > 
> > > Hi Romain
> > >
> > > > From: Romain Manni-Bucau <rm...@gmail.com>
> > > >
> > > > Hi Xavier,
> > > >
> > > > 2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> > > >
> > > > > Hi,
> > > > >
> > > > > When using ApplicationComposer, I can declare mocks in my test classes
> > > > > like that:
> > > > >
> > > > > @Mock
> > > > > MyDependency myDependency;
> > > > >
> > > > > @MockInjector
> > > > > public Class<?> mockInjector() { return MockitoInjector.class; }
> > > > >
> > > > > But that does not seem to work with (un@Named) qualified mocks:
> > > >
> > > > Hmm, MockitoInjector has no link with CDI, it is for EJB injections
> > > (which
> > > > are not using CDI/@Inject)
> > > >
> > > > > @Mock
> > > > > @MyQualifier
> > > > > MyDependency myDependency;
> > > >
> > > > This looks like a cdi injection but @Inject is missing
> > >
> > > No, this is no CDI injection, it is the declaration of the qualified mock
> > > in my test class.
> > > That field is set by MockitoAnnotations.initMocks() (through MockInjector).
> > >
> > > > > Of course, I can still make a producer for the qualified mock like this:
> > > > >
> > > > > @Produces
> > > > > @MyQualifier
> > > > > MyDependency myDependency() { return Mockito.mock(MyDependency.class);
> > > }
> > > > >
> > > > > But I wanted to know if that was the only way to do it because, now, I
> > > > > have to programmatically create the mock instead of declaring it with
> > > @Mock.
> > > >
> > > > The openejb-mockito extension only supports @Default and @Any qualifiers
> > > (
> > > > https://github.com/apache/tomee/blob/master/utils/
> > > openejb-mockito/src/main/java/org/apache/openejb/mockito/
> > > MockitoExtension.java)
> > > > but no real blocker to support another one. The only issue is to define
> > > how
> > > > to select an instance based on a qualifier then.
> > > >
> > > > Note: @Named is not a "real" qualifier so you actually used @Default I
> > > think
> > >
> > > When MockRegistry is looping on all the fields of the TestInstance to
> > > gather
> > > the mocks created by MockitoAnnotations.initMocks(), it should also keep
> > > all
> > > qualifiers declared on the field.
> > >
> > > Of course, this is not trivial with stereotypes and qualifiers added at
> > > runtime
> > > through BeforeBeanDiscover.addQualifier() (but all annotations could be
> > > kept and
> > > filtered in the MockitoExtension which has access to the BeanManager).
> > >
> > > MockitoExtension would then need to register a bean for each mock with the
> > > correct type AND qualifiers.
> > >
> > > Here is a full example of what I want to do:
> > >
> > > @RunWith(ApplicationComposer.class)
> > > @Classes(cdi = true, innerClassesAsBean = true)
> > > public class MyTest {
> > >
> > >     public static class MyBean {
> > >
> > >         @Inject
> > >         @MyQualifier
> > >         public MyInterface myInterface;
> > >
> > >         public void printMessage() {
> > >             System.out.println(myInterface.getMessage());
> > >         }
> > >     }
> > >
> > >     public interface MyInterface {
> > >         String getMessage();
> > >     }
> > >
> > >     @Retention(RetentionPolicy.RUNTIME)
> > >     @Qualifier
> > >     public @interface MyQualifier {}
> > >
> > >     @Inject
> > >     private MyBean myBean;
> > >
> > >     @Mock
> > >     @MyQualifier
> > >     private MyInterface myInterface;
> > >
> > >     @Test
> > >     public void test() {
> > >         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
> > >         myBean.printMessage();
> > >         Mockito.verify(myInterface).getMessage();
> > >     }
> > >
> > >     @MockInjector
> > >     public Class<? extends FallbackPropertyInjector> mockInjector() {
> > >         return MockitoInjector.class;
> > >     }
> > > }
> > >
> > > Currently, that does not work because MyBean.myInterface won't be resolved.
> > >
> > > My current workaround is:
> > >
> > > @RunWith(ApplicationComposer.class)
> > > @Classes(cdi = true, innerClassesAsBean = true)
> > > public class MyTest {
> > >
> > >     public static class MockProducer {
> > >
> > >         @Produces
> > >         @MyQualifier
> > >         static MyInterface myInterface = Mockito.mock(MyInterface.class);
> > >     }
> > >
> > >     public static class MyBean {
> > >
> > >         @Inject
> > >         @MyQualifier
> > >         public MyInterface myInterface;
> > >
> > >         public void printMessage() {
> > >             System.out.println(myInterface.getMessage());
> > >         }
> > >     }
> > >
> > >     public interface MyInterface {
> > >         String getMessage();
> > >     }
> > >
> > >     @Retention(RetentionPolicy.RUNTIME)
> > >     @Qualifier
> > >     public @interface MyQualifier {}
> > >
> > >     @Inject
> > >     private MyBean myBean;
> > >
> > >     @Inject
> > >     @MyQualifier
> > >     private MyInterface myInterface;
> > >
> > >     @Test
> > >     public void test() {
> > >         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
> > >         myBean.printMessage();
> > >         Mockito.verify(myInterface).getMessage();
> > >     }
> > > }
> >         

Re: ApplicationComposer and qualified mocks

Posted by Xavier Dury <ka...@hotmail.com>.
OK, I'll try to provide a patch when I can find some time.

> From: Romain Manni-Bucau <rm...@gmail.com>
>     
> I think I get the use case. Do you want to try to patch openejb-mockito?
> 
> 2017-04-05 13:28 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> 
> > Hi Romain
> >
> > > From: Romain Manni-Bucau <rm...@gmail.com>
> > >
> > > Hi Xavier,
> > >
> > > 2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> > >
> > > > Hi,
> > > >
> > > > When using ApplicationComposer, I can declare mocks in my test classes
> > > > like that:
> > > >
> > > > @Mock
> > > > MyDependency myDependency;
> > > >
> > > > @MockInjector
> > > > public Class<?> mockInjector() { return MockitoInjector.class; }
> > > >
> > > > But that does not seem to work with (un@Named) qualified mocks:
> > >
> > > Hmm, MockitoInjector has no link with CDI, it is for EJB injections
> > (which
> > > are not using CDI/@Inject)
> > >
> > > > @Mock
> > > > @MyQualifier
> > > > MyDependency myDependency;
> > >
> > > This looks like a cdi injection but @Inject is missing
> >
> > No, this is no CDI injection, it is the declaration of the qualified mock
> > in my test class.
> > That field is set by MockitoAnnotations.initMocks() (through MockInjector).
> >
> > > > Of course, I can still make a producer for the qualified mock like this:
> > > >
> > > > @Produces
> > > > @MyQualifier
> > > > MyDependency myDependency() { return Mockito.mock(MyDependency.class);
> > }
> > > >
> > > > But I wanted to know if that was the only way to do it because, now, I
> > > > have to programmatically create the mock instead of declaring it with
> > @Mock.
> > >
> > > The openejb-mockito extension only supports @Default and @Any qualifiers
> > (
> > > https://github.com/apache/tomee/blob/master/utils/
> > openejb-mockito/src/main/java/org/apache/openejb/mockito/
> > MockitoExtension.java)
> > > but no real blocker to support another one. The only issue is to define
> > how
> > > to select an instance based on a qualifier then.
> > >
> > > Note: @Named is not a "real" qualifier so you actually used @Default I
> > think
> >
> > When MockRegistry is looping on all the fields of the TestInstance to
> > gather
> > the mocks created by MockitoAnnotations.initMocks(), it should also keep
> > all
> > qualifiers declared on the field.
> >
> > Of course, this is not trivial with stereotypes and qualifiers added at
> > runtime
> > through BeforeBeanDiscover.addQualifier() (but all annotations could be
> > kept and
> > filtered in the MockitoExtension which has access to the BeanManager).
> >
> > MockitoExtension would then need to register a bean for each mock with the
> > correct type AND qualifiers.
> >
> > Here is a full example of what I want to do:
> >
> > @RunWith(ApplicationComposer.class)
> > @Classes(cdi = true, innerClassesAsBean = true)
> > public class MyTest {
> >
> >     public static class MyBean {
> >
> >         @Inject
> >         @MyQualifier
> >         public MyInterface myInterface;
> >
> >         public void printMessage() {
> >             System.out.println(myInterface.getMessage());
> >         }
> >     }
> >
> >     public interface MyInterface {
> >         String getMessage();
> >     }
> >
> >     @Retention(RetentionPolicy.RUNTIME)
> >     @Qualifier
> >     public @interface MyQualifier {}
> >
> >     @Inject
> >     private MyBean myBean;
> >
> >     @Mock
> >     @MyQualifier
> >     private MyInterface myInterface;
> >
> >     @Test
> >     public void test() {
> >         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
> >         myBean.printMessage();
> >         Mockito.verify(myInterface).getMessage();
> >     }
> >
> >     @MockInjector
> >     public Class<? extends FallbackPropertyInjector> mockInjector() {
> >         return MockitoInjector.class;
> >     }
> > }
> >
> > Currently, that does not work because MyBean.myInterface won't be resolved.
> >
> > My current workaround is:
> >
> > @RunWith(ApplicationComposer.class)
> > @Classes(cdi = true, innerClassesAsBean = true)
> > public class MyTest {
> >
> >     public static class MockProducer {
> >
> >         @Produces
> >         @MyQualifier
> >         static MyInterface myInterface = Mockito.mock(MyInterface.class);
> >     }
> >
> >     public static class MyBean {
> >
> >         @Inject
> >         @MyQualifier
> >         public MyInterface myInterface;
> >
> >         public void printMessage() {
> >             System.out.println(myInterface.getMessage());
> >         }
> >     }
> >
> >     public interface MyInterface {
> >         String getMessage();
> >     }
> >
> >     @Retention(RetentionPolicy.RUNTIME)
> >     @Qualifier
> >     public @interface MyQualifier {}
> >
> >     @Inject
> >     private MyBean myBean;
> >
> >     @Inject
> >     @MyQualifier
> >     private MyInterface myInterface;
> >
> >     @Test
> >     public void test() {
> >         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
> >         myBean.printMessage();
> >         Mockito.verify(myInterface).getMessage();
> >     }
> > }
>     

Re: ApplicationComposer and qualified mocks

Posted by Romain Manni-Bucau <rm...@gmail.com>.
I think I get the use case. Do you want to try to patch openejb-mockito?


Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<https://blog-rmannibucau.rhcloud.com> | Old Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | JavaEE Factory
<https://javaeefactory-rmannibucau.rhcloud.com>

2017-04-05 13:28 GMT+02:00 Xavier Dury <ka...@hotmail.com>:

> Hi Romain
>
> > From: Romain Manni-Bucau <rm...@gmail.com>
> >
> > Hi Xavier,
> >
> > 2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> >
> >> Hi,
> >>
> >> When using ApplicationComposer, I can declare mocks in my test classes
> >> like that:
> >>
> >> @Mock
> >> MyDependency myDependency;
> >>
> >> @MockInjector
> >> public Class<?> mockInjector() { return MockitoInjector.class; }
> >>
> >> But that does not seem to work with (un@Named) qualified mocks:
> >
> > Hmm, MockitoInjector has no link with CDI, it is for EJB injections
> (which
> > are not using CDI/@Inject)
> >
> >> @Mock
> >> @MyQualifier
> >> MyDependency myDependency;
> >
> > This looks like a cdi injection but @Inject is missing
>
> No, this is no CDI injection, it is the declaration of the qualified mock
> in my test class.
> That field is set by MockitoAnnotations.initMocks() (through MockInjector).
>
> >> Of course, I can still make a producer for the qualified mock like this:
> >>
> >> @Produces
> >> @MyQualifier
> >> MyDependency myDependency() { return Mockito.mock(MyDependency.class);
> }
> >>
> >> But I wanted to know if that was the only way to do it because, now, I
> >> have to programmatically create the mock instead of declaring it with
> @Mock.
> >
> > The openejb-mockito extension only supports @Default and @Any qualifiers
> (
> > https://github.com/apache/tomee/blob/master/utils/
> openejb-mockito/src/main/java/org/apache/openejb/mockito/
> MockitoExtension.java)
> > but no real blocker to support another one. The only issue is to define
> how
> > to select an instance based on a qualifier then.
> >
> > Note: @Named is not a "real" qualifier so you actually used @Default I
> think
>
> When MockRegistry is looping on all the fields of the TestInstance to
> gather
> the mocks created by MockitoAnnotations.initMocks(), it should also keep
> all
> qualifiers declared on the field.
>
> Of course, this is not trivial with stereotypes and qualifiers added at
> runtime
> through BeforeBeanDiscover.addQualifier() (but all annotations could be
> kept and
> filtered in the MockitoExtension which has access to the BeanManager).
>
> MockitoExtension would then need to register a bean for each mock with the
> correct type AND qualifiers.
>
> Here is a full example of what I want to do:
>
> @RunWith(ApplicationComposer.class)
> @Classes(cdi = true, innerClassesAsBean = true)
> public class MyTest {
>
>     public static class MyBean {
>
>         @Inject
>         @MyQualifier
>         public MyInterface myInterface;
>
>         public void printMessage() {
>             System.out.println(myInterface.getMessage());
>         }
>     }
>
>     public interface MyInterface {
>         String getMessage();
>     }
>
>     @Retention(RetentionPolicy.RUNTIME)
>     @Qualifier
>     public @interface MyQualifier {}
>
>     @Inject
>     private MyBean myBean;
>
>     @Mock
>     @MyQualifier
>     private MyInterface myInterface;
>
>     @Test
>     public void test() {
>         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
>         myBean.printMessage();
>         Mockito.verify(myInterface).getMessage();
>     }
>
>     @MockInjector
>     public Class<? extends FallbackPropertyInjector> mockInjector() {
>         return MockitoInjector.class;
>     }
> }
>
> Currently, that does not work because MyBean.myInterface won't be resolved.
>
> My current workaround is:
>
> @RunWith(ApplicationComposer.class)
> @Classes(cdi = true, innerClassesAsBean = true)
> public class MyTest {
>
>     public static class MockProducer {
>
>         @Produces
>         @MyQualifier
>         static MyInterface myInterface = Mockito.mock(MyInterface.class);
>     }
>
>     public static class MyBean {
>
>         @Inject
>         @MyQualifier
>         public MyInterface myInterface;
>
>         public void printMessage() {
>             System.out.println(myInterface.getMessage());
>         }
>     }
>
>     public interface MyInterface {
>         String getMessage();
>     }
>
>     @Retention(RetentionPolicy.RUNTIME)
>     @Qualifier
>     public @interface MyQualifier {}
>
>     @Inject
>     private MyBean myBean;
>
>     @Inject
>     @MyQualifier
>     private MyInterface myInterface;
>
>     @Test
>     public void test() {
>         Mockito.doReturn("Hello world!").when(myInterface).getMessage();
>         myBean.printMessage();
>         Mockito.verify(myInterface).getMessage();
>     }
> }

Re: ApplicationComposer and qualified mocks

Posted by Xavier Dury <ka...@hotmail.com>.
Hi Romain

> From: Romain Manni-Bucau <rm...@gmail.com>
>     
> Hi Xavier,
> 
> 2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:
> 
>> Hi,
>>
>> When using ApplicationComposer, I can declare mocks in my test classes
>> like that:
>>
>> @Mock
>> MyDependency myDependency;
>>
>> @MockInjector
>> public Class<?> mockInjector() { return MockitoInjector.class; }
>>
>> But that does not seem to work with (un@Named) qualified mocks:
> 
> Hmm, MockitoInjector has no link with CDI, it is for EJB injections (which
> are not using CDI/@Inject)
>
>> @Mock
>> @MyQualifier
>> MyDependency myDependency;
> 
> This looks like a cdi injection but @Inject is missing

No, this is no CDI injection, it is the declaration of the qualified mock in my test class.
That field is set by MockitoAnnotations.initMocks() (through MockInjector).

>> Of course, I can still make a producer for the qualified mock like this:
>>
>> @Produces
>> @MyQualifier
>> MyDependency myDependency() { return Mockito.mock(MyDependency.class); }
>>
>> But I wanted to know if that was the only way to do it because, now, I
>> have to programmatically create the mock instead of declaring it with @Mock.
> 
> The openejb-mockito extension only supports @Default and @Any qualifiers (
> https://github.com/apache/tomee/blob/master/utils/openejb-mockito/src/main/java/org/apache/openejb/mockito/MockitoExtension.java)
> but no real blocker to support another one. The only issue is to define how
> to select an instance based on a qualifier then.
> 
> Note: @Named is not a "real" qualifier so you actually used @Default I think

When MockRegistry is looping on all the fields of the TestInstance to gather 
the mocks created by MockitoAnnotations.initMocks(), it should also keep all
qualifiers declared on the field.

Of course, this is not trivial with stereotypes and qualifiers added at runtime
through BeforeBeanDiscover.addQualifier() (but all annotations could be kept and
filtered in the MockitoExtension which has access to the BeanManager).

MockitoExtension would then need to register a bean for each mock with the
correct type AND qualifiers.

Here is a full example of what I want to do:

@RunWith(ApplicationComposer.class)
@Classes(cdi = true, innerClassesAsBean = true)
public class MyTest {
    
    public static class MyBean {
        
        @Inject
        @MyQualifier
        public MyInterface myInterface;
        
        public void printMessage() {
            System.out.println(myInterface.getMessage());
        }
    }
    
    public interface MyInterface {
        String getMessage();
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface MyQualifier {}
    
    @Inject
    private MyBean myBean;
    
    @Mock
    @MyQualifier
    private MyInterface myInterface;
    
    @Test
    public void test() {
        Mockito.doReturn("Hello world!").when(myInterface).getMessage();
        myBean.printMessage();
        Mockito.verify(myInterface).getMessage();
    }
    
    @MockInjector
    public Class<? extends FallbackPropertyInjector> mockInjector() {
        return MockitoInjector.class;
    }
}

Currently, that does not work because MyBean.myInterface won't be resolved.

My current workaround is:

@RunWith(ApplicationComposer.class)
@Classes(cdi = true, innerClassesAsBean = true)
public class MyTest {
    
    public static class MockProducer {
        
        @Produces
        @MyQualifier
        static MyInterface myInterface = Mockito.mock(MyInterface.class);
    }
    
    public static class MyBean {
        
        @Inject
        @MyQualifier
        public MyInterface myInterface;
        
        public void printMessage() {
            System.out.println(myInterface.getMessage());
        }
    }
    
    public interface MyInterface {
        String getMessage();
    }
    
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface MyQualifier {}
    
    @Inject
    private MyBean myBean;
    
    @Inject
    @MyQualifier
    private MyInterface myInterface;
    
    @Test
    public void test() {
        Mockito.doReturn("Hello world!").when(myInterface).getMessage();
        myBean.printMessage();
        Mockito.verify(myInterface).getMessage();
    }    
}

Re: ApplicationComposer and qualified mocks

Posted by Romain Manni-Bucau <rm...@gmail.com>.
Hi Xavier,


2017-04-05 12:22 GMT+02:00 Xavier Dury <ka...@hotmail.com>:

> Hi,
>
>
> When using ApplicationComposer, I can declare mocks in my test classes
> like that:
>
>
> @Mock
>
> MyDependency myDependency;
>
>
> @MockInjector
> public Class<?> mockInjector() { return MockitoInjector.class; }
>
> But that does not seem to work with (un@Named) qualified mocks:
>
>
Hmm, MockitoInjector has no link with CDI, it is for EJB injections (which
are not using CDI/@Inject)


>
> @Mock
>
> @MyQualifier
>
> MyDependency myDependency;
>

This looks like a cdi injection but @Inject is missing


>
> Of course, I can still make a producer for the qualified mock like this:
>
> @Produces
> @MyQualifier
> MyDependency myDependency() { return Mockito.mock(MyDependency.class); }
>
> But I wanted to know if that was the only way to do it because, now, I
> have to programmatically create the mock instead of declaring it with @Mock.
>
>
The openejb-mockito extension only supports @Default and @Any qualifiers (
https://github.com/apache/tomee/blob/master/utils/openejb-mockito/src/main/java/org/apache/openejb/mockito/MockitoExtension.java)
but no real blocker to support another one. The only issue is to define how
to select an instance based on a qualifier then.

Note: @Named is not a "real" qualifier so you actually used @Default I think



> Thanks,
>
> Xavier
>
>