我有一个类。
public class FeedServiceImpl implements FeedService {
private final Map<FeedType, FeedStrategy> strategyByType;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public FeedServiceImpl(Map<FeedType, FeedStrategy> strategyByType) {
if (strategyByType.isEmpty()) throw new IllegalArgumentException("strategyByType map is empty");
this.strategyByType = strategyByType;
}
@Override
public void feed(LocalDate feedDate, FeedType feedType, String uuid) {
if (!strategyByType.containsKey(feedType)) {
throw new IllegalArgumentException("Not supported feedType: " + feedType);
}
executorService.submit(() -> runFeed(feedType, feedDate, uuid));
}
private FeedTaskResult runFeed(FeedType feedType, LocalDate feedDate, String uuid) {
return strategyByType.get(feedType).feed(feedDate, uuid);
}
}
如何通过 Mockito 验证当我调用源方法时是否调用了strategyByType.get(feedType).feed(feedDate, uuid)
?
@RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
@Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> mock(FeedStrategy.class)));
}
@Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(strategyByType.get(TH_CREDIT), timeout(100)).feed(date, uuid);
}
}
这是我的版本。但我不想使用Mockito超时方法。在我看来,这不是一个好的解决方案。请帮帮我!
当我测试依赖于执行程序的代码时,我通常会尝试使用执行器实现,该实现立即在提交的同一线程中运行任务,以消除多线程的所有麻烦。也许您可以为FeedServiceImpl
类添加另一个构造函数,以便您提供自己的执行器?谷歌番石榴库有一个MoreExecutors.directExecutor()
方法,它将返回这样的执行器。这意味着您的测试设置将更改为以下内容:
service = new FeedServiceImpl(strategyByType, MoreExecutors.directExecutor());
然后,测试代码的其余部分应按原样工作,您可以安全地删除超时验证模式参数。
对 Huss 答案@Per一点改进,因此您不必更改构造函数,您可以使用 ReflectionTestUtils 类,它是 spring-test 包的一部分。
@Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
ReflectionTestUtils.setField(service , "executorService", MoreExecutors.newDirectExecutorService());
}
现在在测试中,您的executorService将实现MoreExecutors.newDirectExecutorService()而不是Executors.newSingleThreadExecutor()。
如果你想运行一个真正的FeedStrategy
(而不是另一个答案中建议的模拟的),那么你需要一种方法让FeedStrategy
以某种方式向测试线程指示它何时完成。例如,通过使用CountDownLatch
或设置测试可以等待的标志。
public class FeedStrategy {
private boolean finished;
public void feed(...) {
...
this.finished = true;
}
public boolean isFinished() {
return this.finished;
}
}
或者也许FeedStrategy
有一些副作用,你可以等待?
如果上述任何一项为真,那么您可以使用 Awaitility 来实现服务员。例如:
@Test
public void aTest() {
// run your test
// wait for completion
await().atMost(100, MILLISECONDS).until(feedStrategyHasCompleted());
// assert
// ...
}
private Callable<Boolean> feedStrategyHasCompleted() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
// return true if your condition has been met
return ...;
}
};
}
对测试显示 FeedStrategy.class 的模拟,以便能够验证是否调用了 FeedStrategy.feed 方法:
@RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
private FeedStrategy feedStrategyMock;
@Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
feedStrategyMock = Mockito.mock();
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> feedStrategyMock));
}
@Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(feedStrategyMock).feed(date, uuid);
}
}