如何在不使用Mockito超时的情况下测试使用ExecutorService的代码?

时间:2017-10-12 14:25:58

标签: java unit-testing junit mockito

我上课了。

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);
    }
}

当我调用Feed方法时,如何与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超时方法。在我看来,这不是一个好的解决方案。请帮帮我!

4 个答案:

答案 0 :(得分:2)

当我测试依赖于执行程序的代码时,我通常会尝试使用一个执行程序实现,它在提交的同一个线程中立即运行任务,以消除多线程的所有麻烦。也许你可以为你的FeedServiceImpl类添加另一个构造函数,让你提供自己的执行程序?谷歌番石榴lib有一个MoreExecutors.directExecutor()方法,将返回这样一个执行者。这意味着您的测试设置将更改为:

service = new FeedServiceImpl(strategyByType, MoreExecutors.directExecutor());

其余的测试代码应该按原样运行,您可以安全地删除超时验证模式参数。

答案 1 :(得分:2)

@Per Huss答案有一些改进,因此您不必更改构造函数,可以使用 spring-test 包中的 ReflectionTestUtils 类。

@Before
public void setUp() {
    strategyByType = strategyByTypeFrom(TRADEHUB);
    service = new FeedServiceImpl(strategyByType);
    ReflectionTestUtils.setField(service , "executorService", MoreExecutors.newDirectExecutorService());
}

现在在测试中,您的executorService将实现MoreExecutors.newDirectExecutorService()而不是Executors.newSingleThreadExecutor()。

答案 2 :(得分:1)

如果您想运行真实的 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 ...;
            }
        };
    }

答案 3 :(得分:0)

您需要为您的测试显示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);
}
}