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

Sou*_*Cub 2 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();
    }

}
Run Code Online (Sandbox Code Playgroud)

那是在做一些计算逻辑.为此,我还有spring配置:

@Configuration
public class RewardDurationCalculatorConfiguration {

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

ano*_*ode 8

我倾向于同意你的同事.

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

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

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

当您通过此定义提升整个Spring上下文时,它不再是单元测试,而是集成测试,因为您还在测试整个Spring配置,引导,自动装配等,即类的集成进入Spring应用程序.