Search code examples
javajmockit

JMockit: how to set properties on injectable?


Consider the following code:

@Tested
CodeToTest codeToTest;

@Injectable
Injected injected;

@Test
public void test() {
  new Expectations() {{ ... }}
  assertThat(codeToTest.memberVariable).isEqualTo("x");
}

//

public class CodeToTest { public CodeToTest(Injected injected) { memberVariable = injected.getProperty("x") } }

I want to test CodeToTest. CodeToTest needs Injected to be injected into it's constructor. How do I set a property such as injected.setProperty("x") so that it is available for access in CodeToTest?


Solution

  • The test at hand should be covering a specific method of CodeToTest; the constructor should have its own tests like your test does. So for example, if the constructor is setting a field according to what gets passed in, like this:

    public class Bar {
        public int getValue() {
            return 8675309;
        }
    }
    
    public class Foo {
        public String field = null;
    
        public Foo(Bar b) {
            this.field = "" + b.getValue();
        }
    
        public String getField() {
            return this.field;
        }
    
        public char getFirstChar() {
            return getField().charAt(0);
        }
    }
    

    Here I am setting a String field according to an int found in a Bar that was passed into the constructor. I wish to unit test my getFirstChar() method ...

    @Tested Foo foo;
    
    @Injectable Bar bar;
    
    @Test
    public void test() throws Exception {
    // ...
    }
    

    Now, as you point out, in this case my field has already been set before test() even starts. So I have two choices here: Firstly, since I am pulling out the field based on its getter, I can partially mock my class being tested:

    @Test
    public void test() throws Exception {
        new Expectations(foo) {{
            foo.getField(); result = "24601";
        }};
    
        char c = foo.getFirstChar();
    
        assertThat(c, is('2'));
    }
    

    OR, if you don't want to do this or if you are doing direct field access rather than via getter, you can use Deencapsulation (part of JMockit) to set the internal field and then test:

    @Test
    public void test() throws Exception {
        Deencapsulation.setField(foo, "field", "24601");
    
        char c = foo.getFirstChar();
    
        assertThat(c, is('2'));
    }
    

    And of course, I test my constructor separately:

    @Test
    public void testConstructor() throws Exception {
        new Expectations() {{
            bar.getValue(); result = 24601;
        }};
    
        Foo foo2 = new Foo(bar);
        String fieldValue = foo2.getField(); // or use Deencapsulation
        assertThat(fieldValue, is("24601"));
    }
    

    Hope this helps, and good luck!