Injecting a String property with @InjectMocks

Viewed 46427

I have a Spring MVC @Controller with this constructor:

@Autowired
public AbcController(XyzService xyzService, @Value("${my.property}") String myProperty) {/*...*/}

I want to write a standalone unit test for this Controller:

@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {

    @Mock
    private XyzService mockXyzService;

    private String myProperty = "my property value";

    @InjectMocks
    private AbcController controllerUnderTest;

    /* tests */
}

Is there any way to get @InjectMocks to inject my String property? I know I can't mock a String since it's immutable, but can I just inject a normal String here?

@InjectMocks injects a null by default in this case. @Mock understandably throws an exception if I put it on myProperty. Is there another annotation I've missed that just means "inject this exact object rather than a Mock of it"?

7 Answers

Since you're using Spring, you can use the org.springframework.test.util.ReflectionTestUtils from the spring-test module. It neatly wraps setting a field on a object or a static field on a class (along with other utility methods).

@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {

    @Mock
    private XyzService mockXyzService;

    @InjectMocks
    private AbcController controllerUnderTest;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(controllerUnderTest, "myProperty", 
               "String you want to inject");
    }

    /* tests */
}

Just don't use @InjectMocks in that case.

do:

@Before
public void setup() {
 controllerUnderTest = new AbcController(mockXyzService, "my property value");
}

Solution is simple: You should put constructor injection for the object type while for primitive/final dependencies you can simply use setter injection and that'll be fine for this scenario.

So this:

public AbcController(XyzService xyzService, @Value("${my.property}") String myProperty) {/*...*/}

Would be changed to:

@Autowired
public AbcController(XyzService xyzService) {/*...*/}

@Autowired
public setMyProperty(@Value("${my.property}") String myProperty){/*...*/}

And the @Mock injections in test would be as simple as:

@Mock
private XyzService xyzService;

@InjectMocks
private AbcController abcController;

@BeforeMethod
public void setup(){
    MockitoAnnotations.initMocks(this);
    abcController.setMyProperty("new property");
}

And that'll be enough. Going for Reflections is not advisable!

PLEASE AVOID THE USAGE OF REFLECTIONS IN PRODUCTION CODE AS MUCH AS POSSIBLE!!

For the solution of Jan Groot I must remind you that it will become very nasty since you will have to remove all the @Mock and even @InjectMocks and would have to initialize and then inject them manually which for 2 dependencies sound easy but for 7 dependencies the code becomes a nightmare (see below).

private XyzService xyzService;
private AbcController abcController;

@BeforeMethod
public void setup(){ // NIGHTMARE WHEN MORE DEPENDENCIES ARE MOCKED!
    xyzService = Mockito.mock(XyzService.class);
    abcController = new AbcController(xyzService, "new property");
}

What you can use is this : org.mockito.internal.util.reflection.Whitebox

  1. Refactor your "AbcController" class constructor
  2. In your Test class "before" method, use Whitebox.setInternalState method to specify whatever string you want

    @Before
    public void setUp(){
        Whitebox.setInternalState(controllerUnderTest, "myProperty", "The string that you want"); }
    
Related