如何在@PostConstruct中模拟bean?

时间:2019-01-15 16:32:20

标签: java spring spring-boot junit

我想模拟@PostConstruct内部的方法调用。 在正常应用程序启动期间,这将从数据库中初始化一些数据。

但是在测试或集成测试中,我想模拟该数据库调用,并返回模拟的Set<String>

问题:@PostConstruct总是在@Before方法中设置模拟之前被调用:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MockITest {
    @MockBean
    private DatabaseService db;

    @Autowird
    private MyService service; //the service to test

    @Before
    public void setup() {
        given(db.findAllByName()).willReturn(Set.of("john", "doe"));
    }

    @Test
    public void testServiceInPostConstructIsMocked() {
        service.run();
    }
}

public class MyService {
    @Autowired
    private DatabaseService db;

    private Set<String> names;

    //initialization on startup, and cache the results in the member field
    @PostConstruct
    public void init() {
        names = db.findAllByName();     
    }

    public void run() {
        System.out.println(names); //always empty for the test
    }
}

如何仍能正确模拟数据库服务?

1 个答案:

答案 0 :(得分:0)

根本原因似乎是在进行任何弹簧初始化并且后处理完成之后应用了@MockBean。因此也在@PostConstruct之后。

因此,我将“名称”缓存移入数据库服务本身,如下所示:

@Service
public class DatabaseService {
    private Set<String> names;

    public Set<String> findAllByName() {
        if (names == null) {
            names = dao.executeSql(...);
        }
        return names;
    }
}


public class MyService {
    public void run() {
        //now when this is called, the mock is already available
        System.out.println(db.findAllByName());
    }
}

试图将内容缓存在MyService中也许是不好的代码,这就是为什么junit和mockito强制将其实现为不同的方式?