Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject strings via Mockito

In my class under the test BusinessFlow I have some private service fields and one private field with java String type.

In my test class I have

@RunWith(MockitoJUnitRunner.class)
public class BusinessFlowTest {
    // How can I mock or spy this?
    private String code = "codeValue";
    @Mock
    private Service1Api service1;
    @Mock
    private Service2Api service2;
    @InjectMocks
    private BusinessFlow flow;
    ...
}

@InjectMocks and @Mock annotations greatly create mocks and inject service fields. But how can I create a mock for code field as Mockito doesn't allow to create a mock for final classes?

One option I see is to use CharSequence interface instead of String field type but it needs to change code of BusinessFlow class and I don't like this idea.

UPDATE: class BusinessFlow is defined like this

@Service
public class BusinessFlow {
    @Autowired
    @Qualifier(value = "clientCode")
    private String code;

    @Autowired
    private Service1Api service1;
    @Autowired
    private Service2Api service2;

    ...
}

and for some reason we don't use Spring Integration tests ability and don't want to rework BusinessFlow in different type of injection (e.g. ctor injection)

like image 794
Andriy Kryvtsun Avatar asked May 04 '26 06:05

Andriy Kryvtsun


2 Answers

Mocks are meant to mimic external dependencies which are either too prohibitive to spin up in a test context, or otherwise unnecessary to do so. You mock things like services and DAO layer access points to ensure all you're doing is a unit test.

What you're wanting to do is to change the value of this field for every test you run, independent of your mocks. Remember - those mocks are external dependencies. You can easily control* the string coming into your class.

For an example in your test:

@Test
public void testWithFoo() {
    // given
    flow.setValue("foo");

    // when
    // invoke a pertinent method

    // then
    // observe results
}

*: And if you can't, this is a good opportunity to refactor.

like image 73
Makoto Avatar answered May 05 '26 18:05

Makoto


It's not wise to mock data objects, or simple classes with repeatable or stateless behaviour that you know to work (like those provided by the JDK).

Field injection is discouraged because of the exact issue you are experiencing.


However if you choose to ignore this received wisdom, Spring does provide a utility class for this purpose: org.springframework.test.util.ReflectionTestUtils.

With this you could inject into your class with:

 BusinessFlow flow = new BusinessFlow();
 ReflectionTestUtils.setField(flow, "code", "testcode");
 ReflectionTestUtils.setField(flow, "service1", mockService);

Of course you can achieve the same thing directly using the Java Reflection API, but this is slightly more convenient.

like image 37
slim Avatar answered May 05 '26 20:05

slim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!