简体   繁体   中英

How to mock static access to spring-managed @Bean with Mockito?

I have a @Service bean that I need static access to:

@Service
public class MyBean implements InitializingBean {
    private static MyBean instance;

    @Override
    public void afterPropertiesSet() throws Exception {
        instance = this;
    }

    public static MyBean get() {
        return instance;
    }

    public String someMethod(String param) {
       return "some";
    }
}

Usage:

@Service
public class OtherService {
    public static void makeUse() {
        MyBean myBean = MyBean.get();
    }
}

Problem: when I write an integration junit test for OtherService that makes use of the stat MyBean access, the instance variable is always null.

@RunWith(SpringRunner.class)
@SpringBootTest
public class ITest {
    @Autowired
    private OtherService service;

    @MockBean
    private MyBean myBean;

    @Before
    public void mock() {
        Mockito.when(myBean.someMethod(any()).thenReturn("testvalue");
    }

    @Test
    public void test() {
        service.makeUse(); //NullPointerException, as instance is null in MyBean
    }
}

Question: how can I write an integration test when using such type of static access to a spring-managed bean?

If you want to influence the @Bean -creation, then use @Configuration

@Configuration
public class MyConfig {

    @Bean
    public MyBean myBean() {
        return new MyBean;
    }

    @Bean
    public OtherService otherService () {
        return new OtherService(myBean());
    }
}

Then mocking becomes really easy:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ITest {
    @MockBean
    private MyBean myBean;

    @Autowired
    private OtherService service;

    @Test
    public void test() {
        // service is initialised with myBean
        ...
    }
}

When more control is needed, then you can choose the following approach. It provides sometimes more control than just a @MockBean. In your test you can easily mock a method just before calling it. This is my preferred way.

@Configuration
public class MyConfig {
    ...    
    @Bean
    public MyBean getMyBean() {
        return mock( MyBean.class);
    }
    @Bean
    public OtherService otherService () {
        return new OtherService( getMyBean());
    }
}

In the application you can use @Autorwired to access it AND implement method overrule/mocking easily.

@RunWith(SpringRunner.class)
@SpringBootTest
public class AnotherTest {
    @Autowired
    private MyBean myBean;

    @Autowired
    private OtherService service;

    @Test
    public void test() {
        when( myBean.method( a, b)).thenReturn( something);
        service.doSomething( ....); // with myBean.method() overruled 
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM