'Why can't mock static method in Spring condition

this is my code.

class ACondition extends SpringBootConditoin {
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if (Config.isA()) {
            return new ConditionOutcome(true, "ok");
        } else {
            return new ConditionOutcome(false, "error");
        }
    }
}

class BCondition extends SpringBootConditoin {
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if (Config.isA()) {
            return new ConditionOutcome(false, "error");
        } else {
            return new ConditionOutcome(true, "ok");
        }
    }
}

@Service
@Conditional(ACondition.class)
class APolicy implements Policy {
    ...
}

@Service
@Conditional(BCondition.class)
class BPolicy implements Policy {
    ...
}

class PolicyManager {
    @Autowired
    @Getter
    List<Policy> policyList;
    ...
}

the default value of Config.isA() is true. I want to make Config.isA() to return false. so I use Mockito.mockstatic.

@Autowired
PolicyManager manager;

@Test
public void get_B_policy() {
    try(MockedStatic<Config> mocked = Mockito.mockStatic(Config.class) {
        mocked.when(() -> Config.isA()).thenReturn(false);
        List<Policy> policyList = manager.getPolicyList();
        assertEquals(1, policyList.size()); // this is right
        assertTrue(policyList.get(0) instanceof BPolicy); // this is not right
    }
}

Why can't mock the online method?

by the way. If I test the BCondition class, the Config.isA() can be mocked. I can enter the branch which I want. It does not work only in conditional annotation.



Solution 1:[1]

Spring Context is already loaded by the time it is reaching the Test Case. Hence, Manager already has selected APolicy.

If you could move the static mock config before spring context loads, then it should match your expectations.

mocked.when(() -> Config.isA()).thenReturn(false);

One way of doing it is initialising the static Mock like below -

Junit4

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {PolicyManager.class, APolicy.class, BPolicy.class})
public class ConditionTest
{
    @Autowired
    PolicyManager manager;

    static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);

    @BeforeClass
    public static void setup()
    {
        mocked.when(() -> Config.isA()).thenReturn(false);
    }

    @AfterClass
    public static void clear()
    {
        mocked.close();
    }

    @Test
    public void get_B_policy() {
        List<Policy> policyList = manager.getPolicyList();
        assertEquals(1, policyList.size()); // this is right
        assertTrue(policyList.get(0) instanceof BPolicy); // should work now
    }
}

Junit5 Please use Jupiter's annotations.

@Autowired
PolicyManager manager;

static MockedStatic<Config> mocked = Mockito.mockStatic(Config.class);

@BeforeAll
public static void setup() { 
    mocked.when(() -> Config.isA()).thenReturn(true);
}

@AfterAll
public static void clear() {
    mocked.close();
}

@Test
public void get_B_policy() {
    List<Policy> policyList = manager.getPolicyList();
    assertEquals(1, policyList.size()); // this is right
    assertTrue(policyList.get(0) instanceof BPolicy); // should work now
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1