当前位置: 首页 > 知识库问答 >
问题:

Spring Retry JUnit:使用自定义重试策略测试重试模板

公孙霖
2023-03-14

我试图测试使用自定义重试策略的重试模板。为了做到这一点,我使用以下示例:

https://github.com/spring-projects/spring-retry/blob/master/src/test/java/org/springframework/retry/support/retrytemplatetests.java#l57

基本上,我的目标是在得到一些特定的http错误状态(例如HTTP500错误状态)时测试我的重试逻辑。

这是我的JUnit的xml上下文:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:int="http://www.springframework.org/schema/integration"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-http="http://www.springframework.org/schema/integration/http"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/integration
         http://www.springframework.org/schema/integration/spring-integration.xsd
         http://www.springframework.org/schema/integration/http
         http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">

    <bean id="retryTemplate_test" class="org.springframework.retry.support.RetryTemplate">
        <property name="retryPolicy">
            <bean
                class="util.CustomRetryPolicy">
                <property name="maxAttempts" value="5" />
            </bean>
        </property>
        <property name="backOffPolicy">
            <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
                <property name="initialInterval" value="1000" />
                <property name="multiplier" value="6" />
            </bean>
        </property>
    </bean>

</beans>

其中CustomRetryPolicy类似:

public class CustomRetryPolicy extends ExceptionClassifierRetryPolicy {

    private String maxAttempts;

    @PostConstruct
    public void init() {

        this.setExceptionClassifier(new Classifier<Throwable, RetryPolicy>() {
            @Override
            public RetryPolicy classify(Throwable classifiable) {
                Throwable exceptionCause = classifiable.getCause();
                if (exceptionCause instanceof HttpStatusCodeException) {
                    int statusCode = ((HttpStatusCodeException) classifiable.getCause()).getStatusCode().value();
                    return handleHttpErrorCode(statusCode);
                }
                return neverRetry();
            }
        });
    }

    public void setMaxAttempts(String maxAttempts) {
        this.maxAttempts = maxAttempts;
    }


    private RetryPolicy handleHttpErrorCode(int statusCode) {
        RetryPolicy retryPolicy = null;
        switch(statusCode) {
        case 404 :
        case 500 :
        case 503 :
        case 504 :
            retryPolicy = defaultRetryPolicy();
            break;
        default :
            retryPolicy = neverRetry();
            break;
        }

        return retryPolicy;
    }

    private RetryPolicy neverRetry() {
        return new NeverRetryPolicy();
    }

    private RetryPolicy defaultRetryPolicy() {
        final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
        simpleRetryPolicy.setMaxAttempts(Integer.valueOf(maxAttempts));
        return simpleRetryPolicy;
    }

}
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration(locations = {"classpath:my_context_for_test.xml"})
public class RetryTemplateTest{


      @Autowired
      @Qualifier("retryTemplate_test")
      RetryTemplate retryTemplate_test;

    @Test
    public void testRetry() throws Throwable {
        Map<Class<? extends Throwable>, Boolean> r = new HashMap<>();
        r.put(HttpStatusCodeException.class, true);

            MockRetryCallback callback = new MockRetryCallback();
            callback.setAttemptsBeforeSuccess(5);            
            retryTemplate_test.execute(callback);

            assertEquals(5, callback.attempts);        
    }

    private static class MockRetryCallback implements RetryCallback<Object, HttpStatusCodeException> {

        private int attempts;

        private int attemptsBeforeSuccess;


        @SuppressWarnings("serial")
        @Override
        public Object doWithRetry(RetryContext status) throws HttpStatusCodeException {
            this.attempts++;
            if (this.attempts < this.attemptsBeforeSuccess) {
                System.out.println("I'm here: "+ this.attempts);
                throw new HttpStatusCodeException(HttpStatus.INTERNAL_SERVER_ERROR) {
                };
            }
            return null;
        }

        public void setAttemptsBeforeSuccess(int attemptsBeforeSuccess) {
            this.attemptsBeforeSuccess = attemptsBeforeSuccess;
        }
    }

}

[更新]

如果我尝试复制这个junit,那么我得到了同样的异常。更具体地说,当尝试在MockRetryCallback类中实例化异常时,它将失败:

    private Exception exceptionToThrow = new Exception();

共有1个答案

魏旭
2023-03-14

我能用这个让它工作:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class HttpRetryTest{


      @Autowired
      @Qualifier("retryTemplate_test")
      RetryOperations retryTemplate_test;

    @Test
    public void testRetry() throws Throwable {
        Map<Class<? extends Throwable>, Boolean> r = new HashMap<>();
        r.put(HttpStatusCodeException.class, true);

            MockRetryCallback callback = new MockRetryCallback();
            MockRetryCallback.attemptsBeforeSuccess =5;            
            retryTemplate_test.execute(callback);

            assertEquals(5, MockRetryCallback.attempts);        
    }

    private static class MockRetryCallback implements RetryCallback<Object, HttpStatusCodeException> {

        private static int attempts;

        private static int attemptsBeforeSuccess;


        @SuppressWarnings("serial")
        @Override
        public Object doWithRetry(RetryContext status) throws HttpStatusCodeException {
            MockRetryCallback.attempts++;
            if (MockRetryCallback.attempts <= MockRetryCallback.attemptsBeforeSuccess) {
                System.out.println("I'm here: "+ MockRetryCallback.attempts);
                throw new HttpStatusCodeException(HttpStatus.INTERNAL_SERVER_ERROR) {
                };

            }
            return null;
        }
    }
}
 类似资料:
  • 在RetryTemplate里面,执行excuter方法是重试还是失败是由RetryPolicy决定的,这也是一个RetryContext工厂.这个RetryTemplate有责任使用当前的策略创建一个RetryContext并且把它注入到RetryCallback在每一次尝试中。回调失败后RetryTemplate必须由RetryPolicy决定使其更新状态(存储在RetryContext中),

  • 我有一个自定义任务定义来运行每个测试具有特殊设置的特定测试文件。我的任务定义如下: 现在,此设置中的一些测试是不可靠的,我尝试再次运行它们,如下所示: 我编写了一个测试类,第一次总是失败,第二次总是成功: 不幸的是,测试只执行一次,整个测试套件失败。我可以使用中所述的自定义规则成功运行测试https://stackoverflow.com/a/55178053/6059889 有没有办法将测试重试

  • 下面是execute()方法所做的事情

  • Ansible Playbooks 的集成测试 很多时候, 人们问, “我怎样才能最好的将 Ansible playbooks 和测试结合在一起?” 这有很多选择. Ansible 的设计实际上是一个”fail-fast”有序系统, 因此它可以很容易地嵌入到 Ansible playbooks. 在这一章节, 我们将讨论基础设施的集成测试及合适的测试等级. Note 这是一个关于测试你部署应用程序

  • 我想用Spring集成创建一个简单的IntegrationFlow,但我遇到了一些困难。 我想创建一个集成流,从Rabbit Mq中的队列中获取消息并将消息发布到endpointRest。 我要处理的问题是,当一个请求失败时,它会继续无休止地重试,如何在这段代码中实现重试策略?例如,我想要3次重试,第一次重试在1秒后,第二次重试在5秒后,第三次重试在1分钟后。

  • 我有一个spring-boot应用程序,它有一个POST REST API,实习生可以使用JPA和Hikari将数据插入到Postgresql中。由于Post请求的数量很大,我计划在JPA内置方法(如save()、saveAll()、findAll()等)上实现Spring重试,