3

I have a project with CDI and I would like to create unit test with mocks. To manage mocks, I would like to use EasyMock and to run with CDI, I find the cdi-unit project which seem easy to use.

I have a problem to get a mock with EasyMock in CDI context. Here is my unit test:

@RunWith(CdiRunner.class)
@AdditionalClasses(MockFactory.class)
public class ResultYearMServiceImplTest {

    @Inject
    private IStockDao stockDao;

    @Inject
    private ResultYearMServiceImpl resultYearMService;

    @Test
    public void getResultList() {
        EasyMock.reset(stockDao);
        EasyMock.expect(stockDao.getListStocks()).andReturn(null).once()
                .andReturn(new ArrayList<DtoStock>()).once();
        EasyMock.replay(stockDao);
    }
}

IStockDao needs to be mock in the test, so to get it I would like to use a @Produces method like this (in MockFactory class given to cdi unit by @AdditionalClasses):

@Produces
@ApplicationScoped
public IStockDao getStockDao() {
    return EasyMock.createMock(IStockDao.class);
}

When I run my unit test, the mock is good in unit test but I get this error:

java.lang.IllegalArgumentException: Not a mock: org.jboss.weld.proxies.IStockDao$-1971870620$Proxy$_$$_WeldClientProxy

This one comes because CDI doesn't give an instance of EasyMock IStockDao but a proxified instance and EasyMock doesn't accept this in these methods (like reset method).

So I replace @ApplicationScoped in MockFactory by @Dependent which doesn't proxified the instance but I have a new problem:

This annotation give a new instance of mock at each injection point so I can use it because I have a mock in the unit test to mock method called in the tested class. And this instance of mock must be the same in the tested class (it's not the case with @Dependent).

How can I get the same instance in the unit test and the tested class ?

Thanks.

Jens Piegsa
  • 7,399
  • 5
  • 58
  • 106
Kiva
  • 9,193
  • 17
  • 62
  • 94
  • Why don't you just create your mock in the test, and create the object to test using `new ResultYearMServiceImpl(stockDao)`? This is the whole point of dependency injection: being able to manually inject mock dependencies in unit tests. You don't need CDI to unit test a class. – JB Nizet May 26 '13 at 17:40
  • I don't use CDI for unit test, I use CDI for the whole project. The ResultYearMServiceImpl is itself inject by CDI in many class when the application run. So I need to keep cdi for unit test without modify my constructor to unit test needs – Kiva May 26 '13 at 17:46
  • You're using Dependency Injection. So Inject your Dependency. You can use a constructor, a setter, or reflaction tricks to acces the injected field. If your mocking framework doesn't support these reflection tricks, it's not a wise choice to use field injection. Mockito support field injection fine using its annotations. – JB Nizet May 26 '13 at 17:50
  • EasyMock works fine with Spring (I use it in another project) so to avoid to change for each project, I'd prefer to use the same framework. I know Mockito does it but my question is: is it possible with EasyMock ? – Kiva May 26 '13 at 17:53

3 Answers3

3

Needle is your friend for testing CDI.

http://needle.spree.de

public class ResultYearMServiceImplTest {
   @Rule
   public final NeedleRule needle = new NeedleRule();

   @Inject
   private IStockDao stockDao;

   @ObjectUnderTest
   private ResultYearMServiceImpl resultYearMService;
@Test
public void getResultList() {
    EasyMock.reset(stockDao);
    EasyMock.expect(stockDao.getListStocks()).andReturn(null).once()
            .andReturn(new ArrayList<DtoStock>()).once();
    EasyMock.replay(stockDao);
}
}
Jan Galinski
  • 11,768
  • 8
  • 54
  • 77
1

I was unit testing a CDI interceptor with easymock and had the same problem as you.

I would like to share the workaround I used. It consists in producing the mocks in @Dependent scope. This way we can get over the CDI proxy problem with easymock.

import static org.easymock.EasyMock.createStrictControl;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;


/**
 * Mock producer. Beans are produced in Dependent scope to not be proxied.
 */
@ApplicationScoped
public class CdiMockProducerUnitTests {

    /**
     * Mock
     */
    private final MyMockClass myMock;

    /**
     * Constructor creating mocks.
     */
    public CdiMockProducerTestesUnitarios() {
        myMock =  createStrictControl().createMock(MyMockClass.class);
    }

    /**
     * Produces mock in dependent scope.
     *
     * @return mock
     */
    @Produces
    @Dependent
    public MyMockClass produceMock() {
        return myMock;
    }

}
marciopd
  • 132
  • 11
  • Yes I found this solution too but I think it's a very dirty solution. Here we have only once mock for the class, so one variable to create (myMock). How do you do when you have 50/60 to create in a very big project ? Do you create 50 variables ? This solution is good to workaround the problem for a small project but no for a big project. – Kiva May 30 '13 at 14:02
  • I agree and think the worst part is having to write the producer methods. In big projects you'll still have to write hundreds of producers, one for each mock type. So, hope an easymock CDI extension is released soon. I found this one http://junitcdi.sandbox.seasar.org/junitcdi-easymock/index.html, but it's not very updated. – marciopd May 31 '13 at 22:41
  • I created a new feature request in EasyMock's JIRA: https://jira.codehaus.org/browse/EASYMOCK-126 . If you agree, please vote. – marciopd Jul 09 '13 at 01:09
1

The next version of CDI-Unit (2.1.1) adds support for EasyMock in the same way that Mockito is currently supported.

Bryn
  • 487
  • 3
  • 11