Testing Hystrix Exception Handling Configurations in Netflix/Hystrix
This test suite validates the exception handling behavior in Netflix’s Hystrix library, specifically focusing on default and override configurations for exception propagation and fallback mechanisms. It ensures proper implementation of Hystrix’s error handling annotations and runtime exception management.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
netflix/hystrix
hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultRaiseHystrixExceptionsTest.java
package com.netflix.hystrix.contrib.javanica.test.common.error;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import rx.Observable;
import rx.observers.TestSubscriber;
/**
* Created by Mike Cowan
*/
public abstract class BasicDefaultRaiseHystrixExceptionsTest {
private Service service;
@Before
public void setUp() throws Exception {
service = createService();
}
protected abstract Service createService();
@Test(expected = BadRequestException.class)
public void testDefaultIgnoreException() {
service.commandInheritsDefaultIgnoreExceptions();
}
@Test(expected = SpecificException.class)
public void testCommandOverridesDefaultIgnoreExceptions() {
service.commandOverridesDefaultIgnoreExceptions(SpecificException.class);
}
@Test(expected = HystrixRuntimeException.class)
public void testCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
// method throws BadRequestException that isn't ignored
service.commandOverridesDefaultIgnoreExceptions(BadRequestException.class);
}
@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
@Test(expected = BadRequestException.class)
public void testFallbackCommandInheritsDefaultIgnoreException() {
service.commandWithFallbackInheritsDefaultIgnoreExceptions();
}
@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
@Test(expected = SpecificException.class)
public void testFallbackCommandOverridesDefaultIgnoreExceptions() {
service.commandWithFallbackOverridesDefaultIgnoreExceptions(SpecificException.class);
}
@Test(expected = HystrixRuntimeException.class)
public void testFallbackCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
service.commandWithFallbackOverridesDefaultIgnoreExceptions(BadRequestException.class);
}
@Test(expected = HystrixRuntimeException.class)
public void testRaiseHystrixRuntimeException() {
service.commandShouldRaiseHystrixRuntimeException();
}
@Test
public void testObservableRaiseHystrixRuntimeException() {
TestSubscriber<Void> testSubscriber = new TestSubscriber<Void>();
service.observableCommandShouldRaiseHystrixRuntimeException().subscribe(testSubscriber);
testSubscriber.assertError(HystrixRuntimeException.class);
}
@DefaultProperties(ignoreExceptions = BadRequestException.class, raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION})
public static class Service {
@HystrixCommand
public Object commandShouldRaiseHystrixRuntimeException() throws SpecificException {
throw new SpecificException("from 'commandShouldRaiseHystrixRuntimeException'");
}
@HystrixCommand
public Observable<Void> observableCommandShouldRaiseHystrixRuntimeException() throws SpecificException {
return Observable.error(new SpecificException("from 'observableCommandShouldRaiseHystrixRuntimeException'"));
}
@HystrixCommand
public Object commandInheritsDefaultIgnoreExceptions() throws BadRequestException {
// this exception will be ignored (wrapped in HystrixBadRequestException) because specified in default ignore exceptions
throw new BadRequestException("from 'commandInheritsIgnoreExceptionsFromDefault'");
}
@HystrixCommand(ignoreExceptions = SpecificException.class)
public Object commandOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) throws BadRequestException, SpecificException {
if(errorType.equals(BadRequestException.class)){
// isn't ignored because command doesn't specify this exception type in 'ignoreExceptions'
throw new BadRequestException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
// something went wrong, this error is ignored because specified in the command's ignoreExceptions
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
@HystrixCommand(fallbackMethod = "fallbackInheritsDefaultIgnoreExceptions")
public Object commandWithFallbackInheritsDefaultIgnoreExceptions() throws SpecificException {
// isn't ignored, need to trigger fallback
throw new SpecificException("from 'commandWithFallbackInheritsDefaultIgnoreExceptions'");
}
@HystrixCommand
private Object fallbackInheritsDefaultIgnoreExceptions() throws BadRequestException {
// should be ignored because specified in global ignore exception, fallback command inherits default ignore exceptions
throw new BadRequestException("from 'fallbackInheritsDefaultIgnoreExceptions'");
}
@HystrixCommand(fallbackMethod = "fallbackOverridesDefaultIgnoreExceptions")
public Object commandWithFallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
// isn't ignored, need to trigger fallback
throw new SpecificException();
}
@HystrixCommand(ignoreExceptions = SpecificException.class)
private Object fallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
if(errorType.equals(BadRequestException.class)){
// isn't ignored because fallback doesn't specify this exception type in 'ignoreExceptions'
throw new BadRequestException("from 'fallbackOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
// something went wrong, this error is ignored because specified in the fallback's ignoreExceptions
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
}
public static final class BadRequestException extends RuntimeException {
public BadRequestException() {
}
public BadRequestException(String message) {
super(message);
}
}
public static final class SpecificException extends RuntimeException {
public SpecificException() {
}
public SpecificException(String message) {
super(message);
}
}
}