Search code examples
javaunit-testingjunit4jmockit

Is it possible to test a private constructor with a null parameter if one of the constructor arguments is type List?


I am using jMockit for unit testing of some new objects. In my particular case, I am attempting to test a private constructor's reaction to a null parameter. Consider the example:

public abstract class Foo {

    private final String nickname;

    public Foo(final String nickname) {
        // Check condition
        Preconditions.checkNotNull(nickname);
        // Do some stuff
        int i=0; while (i<10) {i++; System.out.println("I can count to "+i);} 
        // Store value
        this.nickname = nickname;
    }

    public boolean isItMyName(String alias) {
        return (nickname.equalsIgnoreCase(alias));
    }

}

with

public class Bar extends Foo {

    private final String professionalNickname;
    private final List<String> jailhouseNicknames = new ArrayList<String>();

    private Bar(final String casualNickname, 
            final String professionalNickname,
            final List<String> jailhouseNicknames) {
        super(casualNickname);
        Preconditions.checkNotNull(professionalNickname);
        Preconditions.checkNotNull(jailhouseNicknames);
        this.professionalNickname = professionalNickname;
        this.jailhouseNicknames.addAll(jailhouseNicknames);
    }

    @Override
    public boolean isItMyName(String alias) {
        if (super.isItMyName(alias)) return true;
        if (professionalNickname.equalsIgnoreCase(alias)) return true;
        for (String nick : jailhouseNicknames)
            if (nick.equalsIgnoreCase(alias)) return true;
        return false;
    }

}

If I want to test the Bar constructor with jMockit, I can use Deencapsulation.newInstance(Class classToInstantiate, Object... nonNullArgs). In my case, however, I want one of the parameters (in particular, one of the Strings) to be a null reference. In that case, I must use the method Deencapsulation.newInstance(Class classToInstantiate, Class[] parameterTypes, Object... initArgs). This would require me to pass in Class< List< String > >, but according to this past answer, this is bad practice and is only possible through casting.

Even if I do throw caution to the wind and attempt something along the lines of

Class<List<String>> listClass = ((Class<List<String>>)new ArrayList<String>().getClass());
Deencapsulation.newInstance(
            Bar.class, 
            new Class<?>[] {String.class, String.class, listClass},
            someString, 
            null, 
            someListOfStrings);

I get the error

Caused by: java.lang.IllegalArgumentException: Specified constructor not found: DiscreteSlot(java.lang.String, java.lang.String, java.util.ArrayList)

Is there a way to do what I'm attempting?


Solution

  • The constructor you are trying to invoke takes a List as the third parameter, so you should pass List.class as the third class in the array in order to invoke it reflectively.

    However, you are passing the result of this expression:

    ((Class<List<String>>)new ArrayList<String>().getClass());
    

    which is equal to

    ArrayList.class
    

    Since there is no constructor which takes an ArrayList, it says no constructor is found.

    Pass List.class instead.