Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

preface

When writing unit tests, is it common to encounter a lot of privately injected variables in the target class to be tested? However, we often don’t omit getters and setters for this private property because the injection of these private properties may be through configuration files or other channels, depending on the environment or other factors at startup, so when we test, It is necessary to test various branch cases by setting different values for this property, but as mentioned earlier, such variables are generally private, and it would be too violent to change the modifier to public/protected for testing purposes….

As a result, the common practice is to write a bunch of reflections. if there is only one place, but if you need to test too many of these scenarios, it is not enough to write, so free your hands.

usage

We’ll start with a service to help:

public class TestServiceImpl {
    private String configId = "11111";

    public String getConfigId(a) {
        returnconfigId; }}Copy the code

Create a new test class:

class TestServiceImplTest {
    private TestServiceImpl testService = new TestServiceImpl();

    @Test
    void getConfigId(a) {
        System.out.println("Before modification :" + testService.getConfigId());
        ReflectionTestUtils.setField(testService,"configId"."2222");
        System.out.println("After modification :"+ testService.getConfigId()); }} Output: Before modification:11111
22:35:40.628 [main] DEBUG org.springframework.test.util.ReflectionTestUtils - Setting field 'configId' of type [null] on target object [com.cf.springboot.TestServiceImpl@45018215] or target class [class com.cf.springboot.TestServiceImpl] to value[2222] Changed to 2222Copy the code

Through the output and code, you can see that it is very simple, a line of code can complete the change of private attributes, is not very convenient!

The principle of

Here is a simple paste code, look at:

public static void setField(@Nullable Object targetObject, @NullableClass<? > targetClass,@Nullable String name, @Nullable Object value, @NullableClass<? > type) { Assert.isTrue(targetObject ! =null|| targetClass ! =null."Either targetObject or targetClass for the field must be specified");
    if(targetObject ! =null && springAopPresent) {
        targetObject = AopTestUtils.getUltimateTargetObject(targetObject);
    }

    if (targetClass == null) {
        targetClass = targetObject.getClass();
    }

    Field field = ReflectionUtils.findField(targetClass, name, type);
    if (field == null) {
        throw new IllegalArgumentException(String.format("Could not find field '%s' of type [%s] on %s or target class [%s]", name, type, safeToString(targetObject), targetClass));
    } else {
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Setting field '%s' of type [%s] on %s or target class [%s] to value [%s]", name, type, safeToString(targetObject), targetClass, value)); } ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, targetObject, value); }}Copy the code