在单元测试中使用SpringRunner是否可以?

时间:2017-10-13 07:48:20

标签: java spring unit-testing spring-test springrunner

我们正与同事争论这种方法。他们说只在集成或功能级别上使用SpringRunner。

问题是在下面的级别使用它的利弊是什么?

例如我有简单的bean:

public class RewardDurationCalculator {

    private Clock clock;

    public OptionalLong calculate(DurationType durationType, List<Pass> passes) {
        long now = Instant.now(clock).getEpochSecond();
        switch (durationType) {
            case FULL_PASS:
                return getCurrentPassDuration(passes, now);
            case TILL_THE_END_OF_THE_CURRENT_ACTIVE_PASS:
                return getTimeInCurrentPassLeft(passes, now);
        }
        return OptionalLong.empty();
    }

    private OptionalLong getCurrentPassDuration(List<Pass> passes, long now) {
        return passes.stream()
                .filter(currentPass(now))
                .mapToLong(Pass::getDuration)
                .findFirst();
    }

    private OptionalLong getTimeInCurrentPassLeft(List<Pass> passes, long now) {
        return passes.stream()
                .filter(currentPass(now))
                .mapToLong(pass -> getEndTs(pass) - now)
                .findFirst();
    }

    private Predicate<Pass> currentPass(long now) {
        return pass -> pass.getStartTs() >= now && now <= getEndTs(pass);
    }

    private long getEndTs(Pass pass) {
        return pass.getStartTs() + pass.getDuration();
    }

}

正在做一些计算逻辑。对于它我也有spring配置:

@Configuration
public class RewardDurationCalculatorConfiguration {

    @Bean
    public RewardDurationCalculator rewardDurationCalculator(Clock clock) {
        return new RewardDurationCalculator(clock);
    }

}

那么为什么我不能像这样编写单元测试:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = RewardDurationCalculatorConfiguration.class)
public class RewardDurationCalculatorTest {

    @MockBean
    private Clock clock;
    @Autowired
    private RewardDurationCalculator rewardDurationCalculator;

    @Test
    public void testCalculateCurrentPassDurationShouldBeReturnedIfPassWasCreatedRightNow() {
        rewardDurationCalculator.calculate(DurationType.FULL_PASS, Collections.emptyList());
    }

}

使用这种方法我可以面对什么?

2 个答案:

答案 0 :(得分:7)

我倾向于同意你的同事。

单元测试应该只测试小的代码单元,通常是方法,理想情况下只运行被测单元而不执行任何其他代码(私有方法除外)。

这样做的一个原因是单元测试应该尽可能快地执行,因此开发人员可以在对代码进行的每次小的更改之后尽可能频繁地运行它们。您希望从单元测试中获得即时反馈。即使加载Spring上下文通常很安静并且只增加了大约一秒的测试执行时间,如果您每天执行测试几百次,这一秒就会变得很烦人,因为例如,一个类的广泛重构。

坚持这条规则的另一个原因是它迫使你编写高度解耦的类。如果你不能为只运用该类而没有其他内容的类编写单元测试,则可能表明你应该重新考虑你的类设计。

当你为测试提升整个Spring上下文时,通过这个定义,它不再是一个单元测试,而是一个集成测试,因为你也在测试整个Spring配置,bootstrapping,autowiring等,i。即将类集成到Spring应用程序中。

答案 1 :(得分:1)

如果你的类使用任何bean依赖项,那么应该使用@SpringRunner,如果你的类是独立的,没有任何应用程序上下文依赖,最好不要使用@SpringRunner。如果我看到你的课程&#39; RewardDurationCalculator&#39;它没有使用任何依赖项,它不应该使用@SpringRunner,甚至不能使用Mocks ......

进一步阅读:

  1. https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/junit4/SpringRunner.html