You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构建Spring Retry自定义测试切片失败,请求排查问题

构建Spring Retry自定义测试切片失败,请求排查问题

正如标题所说,我正在尝试构建一个测试切片,用来测试标注了@Retryable的方法。我知道可以用@SpringBootTest这类启动完整上下文的注解来写测试,但我希望缩小Spring上下文的范围,只初始化需要的Bean来加快测试速度(同时也想借此学习Spring自定义测试切片的知识)。

我首先创建了一个新的@RetryTest注解:

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DefaultTestContextBootstrapper.class)
@ExtendWith({ SpringExtension.class })
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(RetryTypeExcludeFilter.class)
public @interface RetryTest {
    @AliasFor("classes")
    Class<?>[] value() default {};

    @AliasFor("value")
    Class<?>[] classes() default {};
}

同时还写了一个新的TypeExcludeFilter:

public final class RetryTypeExcludeFilter extends StandardAnnotationCustomizableTypeExcludeFilter<RetryTest> {

    private static final Class<?>[] NO_CLASSES = new Class[0];
    private static final Set<Class<?>> DEFAULT_INCLUDES;
    private final Class<?>[] classes;

    RetryTypeExcludeFilter(Class<?> testClass) {
        super(testClass);
        this.classes = (Class<?>[]) this.getAnnotation().getValue("classes", Class[].class).orElse(NO_CLASSES);
    }

    protected Set<Class<?>> getDefaultIncludes() {
        return DEFAULT_INCLUDES;
    }

    protected Set<Class<?>> getComponentIncludes() {
        return new LinkedHashSet<>(Arrays.asList(this.classes));
    }

    static {
        Set<Class<?>> includes = new LinkedHashSet<>();
        includes.add(EnableRetry.class);

        DEFAULT_INCLUDES = Collections.unmodifiableSet(includes);
    }
}

这个过滤器主要是参考WebMvcTypeExcludeFilter写的,我很喜欢它可以指定要实例化的具体控制器Bean的思路,但我把它改成了允许指定被测类。

测试类本身很简单,目前是这样的:

@RetryTest({ ClassUnderTest.class })
class ClassUnderTestTest {

    @Autowired
    ClassUnderTest classUnderTest;

    @Test
    void executeFunction_test() {
        Runnable runnable = mock(Runnable.class);
        doThrow(new RuntimeException("Error message")).when(runnable).run();

        assertThatThrownBy(() -> classUnderTest.executeFunction(runnable, "Error message"))
            .isInstanceOf(CustomException.class);

        verify(runnable, times(3)).run();
    }
}

实际的被测类如下:

@Service
@RequiredArgsConstructor
public class ClassUnderTest {

    @Retryable(retryFor = { RuntimeException.class })
    public <T> T executeFunction(Runnable functionCall, String errorDetails) {
        return functionCall.run();
    }

    @Recover
    public <T> T executeAWSCall(RuntimeException e, Runnable functionCall, String errorDetails) {
        throw new CustomException(e, errorDetails);
    }
}

不过现在我发现这个方案行不通。测试能跑起来,但没有在Spring上下文里运行,所以重试逻辑没有触发(我能看出来,因为抛出的是RuntimeException而不是CustomException)。

我主要怀疑三个地方可能出问题:

  • RetryTypeExcludeFilter里的getDefaultIncludes:说实话我完全不知道它在生命周期里什么时候被调用,也不知道怎么用,更不确定应该在里面包含哪些类。
  • @BootstrapWith:我现在用的是默认测试上下文,因为我没看到WebMvcContext里有什么对重试有用或者必须的内容,但我也不确定这个注解到底有什么用,或者是不是根本不需要它。
  • 自动配置:很多现成的测试切片都会用自动配置和@ImportAutoConfiguration来指定要实例化的Bean,但我不知道要让重试工作需要哪些Bean,所以目前我既没指定任何配置,也没引入现有的自动配置类。

备注:内容来源于stack exchange,提问作者EaterOfFromage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:02:59