Search code examples
javajunitjmockit

JMockit - Instance of class to be tested is null


So I have began using JMockit and JUnit to test my code.

I wrote a class and wanted to test that class. The class is as follows -

public final class KingdomDAOImpl implements KingdomDAO {

private Map<KingdomType, Kingdom> kingdomEmblemMap;

@Inject
public KingdomDAOImpl(final Map<KingdomType, Kingdom> kingdomEmblemMap) {
    this.kingdomEmblemMap = kingdomEmblemMap;
}

@Override
public Map<KingdomType, Kingdom> getKingdoms() {
    return kingdomEmblemMap;
}
}

And my test class looks like this -

public class KingdomDAOTest {

@Injectable
private static Map<KingdomType, Kingdom> kingdomEmblemMap;

@Tested
private static KingdomDAOImpl kingdomDAO;

@BeforeClass
public static void beforeClass() {
    kingdomEmblemMap = new HashMap<>();
}

@Test
public void getKingdomsTest() {
    System.out.println("=========+> " + kingdomDAO);
    final Map<KingdomType, Kingdom> actualKingdomEmblemMap = kingdomDAO.getKingdoms();

    Assert.assertEquals(kingdomEmblemMap, actualKingdomEmblemMap);
}
}

Now, the problem that I'm having is that the kingdomDAO instance is null, which I am not able to understand why. Due to this, kindomDAO.getKingdoms() throws a NullPointerException.

Any ideas on how to proceed from here?

EDIT: I was using JMockit version 1.8. I have tried upgrading to 1.45 and replacing @Injectable annotation with @Tested, but still the same problem persists.


Solution

  • JMockit has changed a bit since the last time I used it.
    What I can recommend to you is, always read the release notes!

    Version 1.42

    JMockit now requires the use of the "-javaagent" JVM initialization parameter

    Version 1.45

    Mocking annotations (@Mocked, @Capturing, and @Injectable) can no longer be used on Java SE collection and map interfaces, for two reasons: 1) on Java 8+, such interfaces include default methods, which do get mocked, easily causing unexpected results in tests; 2) mocking of said interfaces (and more generally, of all value-oriented types which basically encapsulate state without complex behavior in methods) is a well known bad practice. In the particular case of having an @Injectable collection/map field which is meant to be injected into a @Tested object, the test should be changed to use @Tested as well on the collection/map field, with its declaration positioned before the target @Tested field.


    Although I wouldn't code a test like that, the working example is:

    public class KingdomDAOtest {
        @Tested
        Map<KingdomType, Kingdom> kingdomEmblemMap = new HashMap<>();
    
        @Tested
        KingdomDAOImpl kingdomDAO;
    
        @Test
        public void getKingdomsTest() {
            System.out.println("=========+> " + kingdomDAO);
            final Map<KingdomType, Kingdom> actualKingdomEmblemMap = kingdomDAO.getKingdoms();
    
            Assert.assertEquals(kingdomEmblemMap, actualKingdomEmblemMap);
        }
    }