AnsweredAssumed Answered

Activiti CDI module and Alternatives

Question asked by ronnybr on Jan 30, 2013
Latest reply on Jan 31, 2013 by frederikheremans1
Hello Activiti-Community,
I am using CDI in my application quite excessive and I am trying to write my JUnit-tests in a way, that most of my "real" CDI-Beans will be replaced by mocks.

Also, I am using Arquillian, so that the injection works.
The beans.xml for my tests looks like this:

<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="       http://java.sun.com/xml/ns/javaee        http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
  <class>de.data.experts.activiti.mock.DominoProducerMock</class>
  <class>de.data.experts.activiti.mock.PraxisberichtVeroeffentlichenMock</class>
  <class>de.data.experts.activiti.mock.ThemaVeroeffentlichenMock</class>
</alternatives>
</beans>

When I start my JUnit-test it passes a ServiceTask in which I reference a class:

<serviceTask activiti:expression="#{themaVeroeffentlichen.anlegen()}" completionQuantity="1" id="_30" implementation="##WebService" isForCompensation="false" name="Praxisberichtsthema veröffentlichen" startQuantity="1">
      <incoming>_31</incoming>
      <outgoing>_36</outgoing>
    </serviceTask>
The real class is annotated like this:

@Named("themaVeroeffentlichen")
@Dependent
public class ThemaVeroeffentlichen {
And the mock like this:

@Alternative
@Named("themaVeroeffentlichen")
@Dependent
public class ThemaVeroeffentlichenMock extends ThemaVeroeffentlichen {

As you can see in the beans.xml above, I want to replace the "themaVeroeffentlichen"-bean with a mock.
Surprisingly, my test sometimes passes and sometimes I get a NPE, because it calls the real bean and invokes the real method.
I debugged and found the CdiResolver and ProgrammaticBeanLookup classes.
In CdiResolver the method getValue(…) calls the lookup:

  public Object getValue(ELContext context, Object base, Object property) {
    try {
      Object result = getWrappedResolver().getValue(this.context, base, property);
      context.setPropertyResolved(result != null);
      return result;
    } catch (IllegalStateException e) {
      // dependent scoped / EJBs
      Object result = ProgrammaticBeanLookup.lookup(property.toString(), getBeanManager());
      context.setPropertyResolved(result != null);
      return result;
    }
  }

The lookup-method calls the BeanManager with the bean-name and gets a set. From the set the first bean is taken:

  public static Object lookup(String name, BeanManager bm) {
    Iterator<Bean< ? >> iter = bm.getBeans(name).iterator();
    if (!iter.hasNext()) {
      throw new IllegalStateException("CDI BeanManager cannot find an instance of requested type '" + name + "'");
    }
    Bean bean = iter.next();
    CreationalContext ctx = bm.createCreationalContext(bean);
    // select one beantype randomly. A bean has a non-empty set of beantypes.
    Type type = (Type) bean.getTypes().iterator().next();
    return bm.getReference(bean, type, ctx);
  }
And that's why my test sometimes passes and sometimes not. It depends on the order of the beans in the set. Sometimes the mock is first and sometimes the real class.

So, my question is, shouldn't the CdiResolver take another approach when trying to resolve the bean or shouldn't the ProgrammaticBeanLookup throw something like an AmbiguousResolutionException when finding more than one possibility?

Outcomes