Hot questions for Using Enterprise JavaBeans in unit testing

Question:

I am trying to write units tests in Groovy/Spock and have to test the below code.

public class ClassA {
    @Inject
    private ClassB classB;

    @Inject
    private ClassC classC;

    @Inject
    private ClassD classD;

    public void update(final int a, final Map<String, Object> b) {
        classB.executeCommand(classC.callToMethodInClassC(), new InterfaceE<Void>() {
            @Override
            public Void execute() {
                classD.update(a, b);
                return null;
            }
        });
    }
}

Now when I write test code I am unable to reach classD.update(a, b); line. I know how to inject mocks/real object for ClassB, ClassC, ClassD, but unable to reach that statement. Please help.

Note: ClassA.update() & ClassD.update() have no relation between them except that their signatures match.


Answer:

Make your classB mock like this:

Mock(ClassB) {
    executeCommand(_, _) >> { c, e -> e.execute() }
}

I.e. provide a mock implementation for the executeCommand method of ClassB that actually calls the execute method of interfaceE.

EDIT: full working example:

import spock.lang.Specification

class TestMeSpec extends Specification {

    def "my test"() {
        def b = Mock( ClassB ) {
            executeCommand( _, _ ) >> { c, e -> e.execute() }
        }
        def c = Mock( ClassC )
        def d = Mock( ClassD )

        def a = new ClassA( classB: b, classC: c, classD: d )

        when:
        a.update( 0, [ : ] )

        then:
        1 * d.update( 0, [ : ] )
    }
}

class ClassA {
    ClassB classB
    ClassC classC
    ClassD classD

    void update( final int a, final Map<String, Object> b ) {
        classB.executeCommand( classC.callToMethodInClassC(), new InterfaceE<Void>() {
            @Override
            Void execute() {
                classD.update( a, b )
            }
        } )
    }
}

class ClassB {
    void executeCommand( c, InterfaceE e ) {
        println "REAL CLASS B executCommand() METHOD RUNNING"
    }
}

class ClassC {
    def callToMethodInClassC() {}
}

class ClassD {
    void update( a, b ) {
        println "REAL CLASS D update() method called with $a and $b"
    }
}

interface InterfaceE<T> {
    T execute()
}

Question:

I have to test method of some SLSB which invokes another method on current object(using this keyword) and I need to stub it somehow.

Consider following simplified code:

@Local
public interface SomeService{
    public int someMethod();
    public int anotherMethod();
}

@Stateless()
public class SomeServiceImpl{

    @EJB
    private SomeDAO sDAO;

    public SomeServiceImpl(){}

    public SomeServiceImpl(SomeDAO sDAO){
         this.sDAO = sDAO;
    }

    @Override
    public int someMethod(){
        int dbValue = sDAO.getSomeDBValue(); // 1st stub required here
        return dbValue + this.anotherMethod(); // 2nd stub required here
    }

    @Override
    public int anotherMethod(){
         return 5;
    }
}

To stub getSomeDBValue() method I can inject mock to this class with @Mock and @InjectMocks annotations but i can't figure out how to correctly stub anotherMethod(). To stub it I need to do it on mock object for sure, so I tried to pass reference to current object as parameter and in test just pass mocked object. For example if my method would look like this(without need to stub DAO method)..

@Override
public int someMethod(SomeServiceImpl sS){ 
    return sS.anotherMethod(); 
}

My test with manually created mocks would look like this:

@Test
public void someMethodTest() throws Exception {
    SomeServiceImpl sS = mock(SomeServiceImpl.class);
    when(sS.someMethod(any(SomeServiceImpl.class))).thenCallRealMethod();
    when(sS.anotherMethod()).thenReturn(5);
    assertEquals(5, sS.someMethod(sS));
}

Method is invoked on mock object, reference to object itself is passed as parameter and anotherMethod is stubed. It worked but it seems very ugly solution and what if need to inject mock of my DAO using annotations like this:

@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest{

    @Mock
    SomeDAO sDAO;

    //@Mock //I can't use those 2 annotations at once
    @InjectMocks
    SomeServiceImpl sS; 

    @Test
    public void someMethodTest() throws Exception {
        //...   
    }
}

As I understand @InjectMocks annotation is used to indicate class where mocks annotated with @Mock should be injected, but for my ugly solution I need SomeServiceImpl to be mock as well.

Is my solution even close to correct? How I'm suppose to stub anotherMethod() to correctly test someMethod()? Is it a good idea to pass mocked instance of class, which method i test in method argument? If yes, how should I deal with creating mocks with annotations?


Answer:

You should not mock one method while testing another method on the same class. You could theoretically do that (using a Mokito spy for example).

In that sense, you are approaching this on a wrong level: you actually should not care which other methods your method under test calls within your class under test. You see, you want to test that someMethod() does fulfills its contract. If that requires a call to anotherMethod() in your production environment ... how valuable is your unit test, when it mocks anotherMethod() then?!

Another idea: you separate concerns, and move anotherMethod() part into its own class X. And then your class under test could hold an instance of X; and that instance could then be mocked.

Question:

I'm doing some junit tests in my application and I testing a validation method that is in my EJB. I made a test for when the data pass and one test for when the method throw a exception.

This is my junits:

@Test(expected = ValidationException.class)
public void testValidationError() {
    service.validateCsvFile(createCsvContents(false));
}

@Test        
public void testValidationOk() {
    service.validateCsvFile(createCsvContents(true));
}

This is my method:

@Override
public void validateCsvFile(List<CSVDataRow> csvContents) throws MyException {
    List<String> repeatedTransactionIds = new ArrayList<String>();
    Set<String> allTransactionIds = new HashSet<String>();
    for (CSVDataRow cSVDataRow : csvContents) {
        //performing a database size validation
        Validation<CSVDataRow> validation = Validator.validate(cSVDataRow);
        if (validation.hasViolations()){
            throw new ValidationException("Errors found for transactionId: " + cSVDataRow.getTransactionId(), validation.getErrorMessagesWithPropertyPath());
        }
        if (!allTransactionIds.add(cSVDataRow.getTransactionId())){
            repeatedTransactionIds.add(cSVDataRow.getTransactionId());
        }
    }

    if (!repeatedTransactionIds.isEmpty()){
        throw new ValidationException("Duplicated transactionIds found", repeatedTransactionIds.toString());
    }

}

The testValidationError junit do not pass. It throws me this error:

java.lang.Exception: Unexpected exception, expected<xxxxx.backend.exception.ValidationException> but was<javax.ejb.EJBException>
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)

Answer:

By default, when an exception is thrown inside an EJB method the EJB container will wrap it with an EJBException. You can, however, annotate your EJB method with ApplicationException, which will tell the EJB container to throw the application exception directly.

Question:

I'm woking on EJB Project, using JPA to query data. Now, I create unit test and use mockito to mock data. There is a function that I call data from criteria builder, and it's called from @PostConstruct. So if result is empty, then it will throw NoResultException. However, I am unable to run unit test to test it. Take a look on source code below:

For class RefdataUpdateDaoImpl

public class RefdataUpdateDaoImpl{
    public RefdataUpdate getSingleUpdate() {
        CriteriaBuilder cb = getCriteriaBuilder();
        CriteriaQuery<RefdataUpdate> query = cb.createQuery(RefdataUpdate.class);
        Root<RefdataUpdate> rootEntry = query.from(RefdataUpdate.class);

        CriteriaQuery<RefdataUpdate> all = query.select(rootEntry);
        TypedQuery<RefdataUpdate> allQuery = getEntityManager().createQuery(all);
        return allQuery.getSingleResult();
    }
}

In RefDataCacheBean

@PostConstruct
private void postConstruct() throws Exception{
    cachedUpdateTs = RefdataUpdateDaoImpl.getLastUpdate();
}

This is unit test

@Test(expected = NoResultException.class)
    public void testExistingRefDataUpdate() throws NoResultException{
        update.deleteRefDataUpdate();
        refdataServiceBean.getLastUpdate();
    }

So in unit test, it loads data from dataset xml. So when I run test, it supposes to throw NoResultException and test passes, but test fails and console log no entity data found.

Please help to create unit test in this case, when function's called from @PostConstruct.

Trace Logs:

javax.ejb.EJBException: javax.persistence.NoResultException: No entity found for query

Thank you and any comments will be appreciated.


Answer:

Obviously the NoResultException is nested in a EJBException so your test code should be:

@Test(expected = EJBException.class)
public void testExistingRefDataUpdate() throws NoResultException{
    update.deleteRefDataUpdate();
    refdataServiceBean.getLastUpdate();
}