如何对 javanica @HystrixCommand 注释方法进行单元测试?
Posted
技术标签:
【中文标题】如何对 javanica @HystrixCommand 注释方法进行单元测试?【英文标题】:How can I unit-test javanica @HystrixCommand annotated methods? 【发布时间】:2017-09-13 04:35:24 【问题描述】:我正在使用 javanica 并像这样注释我的 hystrix 命令方法:
@HystrixCommand(groupKey="MY_GROUP", commandKey="MY_COMMAND" fallbackMethod="fallbackMethod")
public Object getSomething(Object request)
....
我正在尝试对我的后备方法进行单元测试,而不必直接调用它们,即我想调用 @HystrixCommand
带注释的方法并让它在抛出 500 错误后自然地流入后备。这一切都在单元测试之外起作用。
在我的单元测试中,我使用 springs MockRestServiceServer
返回 500 个错误,这部分工作正常,但 Hystrix 在我的单元测试中未正确初始化。在我的测试方法开始时,我有:
HystrixRequestContext context = HystrixRequestContext.initializeContext();
myService.myHystrixCommandAnnotatedMethod();
在此之后,我尝试按键获取任何 hystrix 命令并检查是否有任何已执行的命令但列表始终为空,我正在使用此方法:
public static HystrixInvokableInfo<?> getHystrixCommandByKey(String key)
HystrixInvokableInfo<?> hystrixCommand = null;
System.out.println("Current request is " + HystrixRequestLog.getCurrentRequest());
Collection<HystrixInvokableInfo<?>> executedCommands = HystrixRequestLog.getCurrentRequest()
.getAllExecutedCommands();
for (HystrixInvokableInfo<?> command : executedCommands)
System.out.println("executed command is " + command.getCommandGroup().name());
if (command.getCommandKey().name().equals(key))
hystrixCommand = command;
break;
return hystrixCommand;
我意识到我在单元测试初始化中遗漏了一些东西,谁能指出我如何正确地进行单元测试的正确方向?
【问题讨论】:
嗨@Oscar,您找到解决方案了吗? @StefanoL 不,找不到任何解决方案。我仍然希望有人有办法做到这一点。 我曾想过编写单独的测试类,称为 XYZHystrixTest,它实际上是在 Hystrix 就位的情况下部分启动 Spring Context。我没有看到任何其他解决方案来解决这个问题。 @StefanoL 测试中的 Hystrix 初始化是我遇到的问题,你是如何解决的? 还没有,但基本上它应该在引导(基本)Spring上下文时出现,对吧? 【参考方案1】:虽然你不一定要 UNIT 测试 hystrix 命令。进行一种弹簧混合测试仍然很有用,我认为在添加注释时接受功能是不正确的。我创建的测试确保断路器在异常时打开。
@RunWith(SpringRunner.class)
@SpringBootTest
public class HystrixProxyServiceTests
@MockBean
private MyRepo myRepo;
@Autowired
private MyService myService;
private static final String ID = “1”;
@Before
public void setup()
resetHystrix();
openCircuitBreakerAfterOneFailingRequest();
@Test
public void circuitBreakerClosedOnSuccess() throws IOException, InterruptedException
when(myRepo.findOneById(USER_ID1))
.thenReturn(Optional.of(Document.builder().build()));
myService.findOneById(USER_ID1);
HystrixCircuitBreaker circuitBreaker = getCircuitBreaker();
Assert.assertTrue(circuitBreaker.allowRequest());
verify(myRepo, times(1)).findOneById(
any(String.class));
@Test
public void circuitBreakerOpenOnException() throws IOException, InterruptedException
when(myRepo.findOneById(ID))
.thenThrow(new RuntimeException());
try
myService.findOneById(ID);
catch (RuntimeException exception)
waitUntilCircuitBreakerOpens();
HystrixCircuitBreaker circuitBreaker = getCircuitBreaker();
Assert.assertFalse(circuitBreaker.allowRequest());
verify(myRepo, times(1)).findOneById(
any(String.class));
private void waitUntilCircuitBreakerOpens() throws InterruptedException
Thread.sleep(1000);
private void resetHystrix()
Hystrix.reset();
private void warmUpCircuitBreaker()
myService.findOneById(USER_ID1);
public static HystrixCircuitBreaker getCircuitBreaker()
return HystrixCircuitBreaker.Factory.getInstance(getCommandKey());
private static HystrixCommandKey getCommandKey()
return HystrixCommandKey.Factory.asKey("findOneById");
private void openCircuitBreakerAfterOneFailingRequest()
ConfigurationManager.getConfigInstance().
setProperty("hystrix.command.findOneById.circuitBreaker.requestVolumeThreshold", 1);
另一件让我困惑的小事是我输入了没有特定命令键的默认注释,但是当创建命令键时,它们是根据我在上面指定的方法名称创建的。对于一个完整的示例,我还添加了注释以表明我没有指定 commandKey。
@HystrixCommand
public Optional<Document> findOneById(final String id)
return this.myRepo.findOneById(id);
希望这对某人有所帮助。
【讨论】:
【参考方案2】:Hystrix 是您接受的功能性工具, 很像 Spring 是一个你接受的工具是功能性的。 你不需要对 Hystrix 调用你的 fallback 方法的能力进行单元测试。
您应该通过在单元测试中直接调用回退方法来对其进行单元测试。
也就是说, 当您希望 Hystrix 调用 fallback 方法时,您可能想要测试 Hystrix 是否真的在调用 fallback 方法; 这不会是单元测试, 这将是一个集成测试。
虽然可以使用 jUnit 编写许多集成测试,但 Hystrix 显然不想参与 jUnit 测试。
我建议您将应用程序安装在开发和/或 qa 测试环境中,并通过在正在运行的系统上强制回退来测试 Hystrix 回退功能。
【讨论】:
我同意编写集成测试的部分,但这是否意味着您建议使用 @RunWith(SpringRunner.class) 编写它,这实际上会启动 spring 上下文,然后我们可以测试实现为如上答案所示。 我不同意,因为有很多方法可以滥用 Hystrix,尤其是注释。例如,通过在 hystrix-wrapped 方法中捕获异常。【参考方案3】:可能为时已晚.. 但是您可以使用该方法在代码运行之前实例化一个
我是这样做的
public void warmUpCircuitBreaker()
HystrixCommandKey commandKey= HystrixCommandKey.Factory.asKey("test");
HystrixCommandProperties.Setter setter = HystrixCommandProperties.defaultSetter();
HystrixPropertiesCommandDefault hystrixPropertiesCommandDefault = new HystrixPropertiesCommandDefault(commandKey, setter);
HystrixCommandGroupKey test = HystrixCommandGroupKey.Factory.asKey("Test");
HystrixCircuitBreaker.Factory.getInstance(commandKey,
test,
hystrixPropertiesCommandDefault,
HystrixCommandMetrics.getInstance(commandKey, test, hystrixPropertiesCommandDefault));
在调用 SUT 之前调用此方法 您也可以设置所有必需的属性
在我的测试中 我正在设置属性以测试不同的场景
ConfigurationManager.getConfigInstance()
.setProperty("hystrix.command.test.circuitBreaker.forceClosed",
true);
【讨论】:
【参考方案4】:我现在遇到过几次问题:如果包装方法的方法签名发生更改,则没有编译时间或引导程序检查回退方法是否仍然可调用。因此,如果一个 int arg 更改为一个 String,而我忘记更改回退方法的签名,那么直到应用程序运行并调用包装的方法时我才会知道。
【讨论】:
它与问题有关吗?以上是关于如何对 javanica @HystrixCommand 注释方法进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
javanica中的Hystrix异步方法不在spring-boot java应用程序中运行