Testing Generic Fallback Method Compatibility in Netflix Hystrix
This test suite validates the generic fallback functionality in Netflix’s Hystrix library, focusing on proper type handling and fallback method definitions. It thoroughly tests both successful and failure scenarios for generic method and class implementations.
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/fallback/BasicGenericFallbackTest.java
package com.netflix.hystrix.contrib.javanica.test.common.fallback;
import com.netflix.hystrix.HystrixEventType;
import com.netflix.hystrix.HystrixInvokableInfo;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException;
import com.netflix.hystrix.contrib.javanica.test.common.BasicHystrixTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import rx.Completable;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import static com.netflix.hystrix.contrib.javanica.test.common.CommonUtils.getHystrixCommandByKey;
import static org.junit.Assert.assertTrue;
/**
* Created by dmgcodevil.
*/
@RunWith(JUnitParamsRunner.class)
public abstract class BasicGenericFallbackTest extends BasicHystrixTest {
@ClassRule
public static final SpringClassRule SCR = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
protected abstract <T> T createProxy(Class<T> t);
public Object[] methodGenericDefinitionSuccess() {
return new Object[]{
new Object[]{MethodGenericDefinitionSuccessCase0.class},
new Object[]{MethodGenericDefinitionSuccessCase1.class},
new Object[]{MethodGenericDefinitionSuccessCase2.class},
new Object[]{MethodGenericDefinitionSuccessCase3.class},
new Object[]{MethodGenericDefinitionSuccessCase4.class},
new Object[]{MethodGenericDefinitionSuccessCase5.class},
new Object[]{MethodGenericDefinitionSuccessCase6.class},
new Object[]{MethodGenericDefinitionSuccessCase7.class},
};
}
public Object[] methodGenericDefinitionFailure() {
return new Object[]{
new Object[]{MethodGenericDefinitionFailureCase0.class},
new Object[]{MethodGenericDefinitionFailureCase1.class},
new Object[]{MethodGenericDefinitionFailureCase2.class},
new Object[]{MethodGenericDefinitionFailureCase3.class},
new Object[]{MethodGenericDefinitionFailureCase4.class},
new Object[]{MethodGenericDefinitionFailureCase5.class},
new Object[]{MethodGenericDefinitionFailureCase6.class},
new Object[]{MethodGenericDefinitionFailureCase7.class},
new Object[]{MethodGenericDefinitionFailureCase8.class},
new Object[]{MethodGenericDefinitionFailureCase9.class},
new Object[]{MethodGenericDefinitionFailureCase10.class},
new Object[]{MethodGenericDefinitionFailureCase11.class},
};
}
public Object[] classGenericDefinitionSuccess() {
return new Object[]{
new Object[]{ClassGenericDefinitionSuccessCase0.class},
new Object[]{ClassGenericDefinitionSuccessCase1.class},
};
}
public Object[] classGenericDefinitionFailure() {
return new Object[]{
new Object[]{ClassGenericDefinitionFailureCase0.class},
new Object[]{ClassGenericDefinitionFailureCase1.class},
};
}
@Test
@Parameters(method = "methodGenericDefinitionSuccess")
public void testMethodGenericDefinitionSuccess(Class<?> type) {
testXKindGenericDefinitionSuccess(type);
}
@Test(expected = FallbackDefinitionException.class)
@Parameters(method = "methodGenericDefinitionFailure")
public void testMethodGenericDefinitionFailure(Class<?> type) {
Object p = createProxy(type);
executeCommand(p);
}
@Test
@Parameters(method = "classGenericDefinitionSuccess")
public void testClassGenericDefinitionSuccess(Class<?> type) {
testXKindGenericDefinitionSuccess(type);
}
@Test(expected = FallbackDefinitionException.class)
@Parameters(method = "classGenericDefinitionFailure")
public void testClassGenericDefinitionFailure(Class<?> type) {
Object p = createProxy(type);
executeCommand(p);
}
private void testXKindGenericDefinitionSuccess(Class<?> type) {
Object p = createProxy(type);
try {
executeCommand(p);
HystrixInvokableInfo<?> command = getHystrixCommandByKey("command");
assertTrue(command.getExecutionEvents().contains(HystrixEventType.FAILURE));
assertTrue(command.getExecutionEvents().contains(HystrixEventType.FALLBACK_SUCCESS));
} catch (FallbackDefinitionException e) {
throw new AssertionError(MessageFormatter.format("Case class '{}' has failed. Reason:\n{}", type.getCanonicalName(), e).getMessage());
}
}
/* ====================================================================== */
/* ===================== GENERIC METHOD DEFINITIONS ===================== */
/* =========================== SUCCESS CASES ============================ */
public static class MethodGenericDefinitionSuccessCase0 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> T command() { throw new IllegalStateException(); }
private <T> T fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase1 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable> T command() { throw new IllegalStateException(); }
private <T extends Serializable> T fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase2 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable, T1 extends T> T1 command() { throw new IllegalStateException(); }
private <T extends Serializable, T1 extends T> T1 fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase3 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable, T1 extends T> GenericEntity<? extends T1> command() { throw new IllegalStateException(); }
private <T extends Serializable, T1 extends T> GenericEntity<? extends T1> fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase4 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable & Comparable, T1 extends T> GenericEntity<? extends T1> command() { throw new IllegalStateException(); }
private <T extends Serializable & Comparable, T1 extends T> GenericEntity<? extends T1> fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase5 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable & Comparable, T1 extends GenericEntity<T>> GenericEntity<T1> command() { throw new IllegalStateException(); }
private <T extends Serializable & Comparable, T1 extends GenericEntity<T>> GenericEntity<T1> fallback() { return null; }
}
public static class MethodGenericDefinitionSuccessCase6 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> GenericEntity<T> command() { throw new IllegalStateException(); }
private <T> GenericEntity<T> fallback() { return new GenericEntity<T>(); }
}
public static class MethodGenericDefinitionSuccessCase7 {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<? super Serializable> command() { throw new IllegalStateException(); }
private GenericEntity<? super Serializable> fallback() { return null; }
}
/* ====================================================================== */
/* ===================== GENERIC METHOD DEFINITIONS ===================== */
/* =========================== FAILURE CASES ============================ */
public static class MethodGenericDefinitionFailureCase0 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> T command() { throw new IllegalStateException(); }
private String fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase1 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable> T command() { throw new IllegalStateException(); }
private <T> T fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase2 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable> T command() { throw new IllegalStateException(); }
private <T extends Comparable> T fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase3 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable, T1 extends T> GenericEntity<? extends T1> command() { throw new IllegalStateException(); }
private <T extends Serializable, T1 extends T> GenericEntity<T1> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase4 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable & Comparable, T1 extends T> GenericEntity<? extends T1> command() { throw new IllegalStateException(); }
private <T extends Serializable, T1 extends T> GenericEntity<? extends T1> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase5 {
@HystrixCommand(fallbackMethod = "fallback")
public <T extends Serializable & Comparable, T1 extends GenericEntity<T>> GenericEntity<T1> command() { throw new IllegalStateException(); }
private <T extends Serializable & Comparable, T1 extends GenericEntity<String>> GenericEntity<T1> fallback() { return null;}
}
public static class MethodGenericDefinitionFailureCase6 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> GenericEntity<T> command() { throw new IllegalStateException(); }
private GenericEntity<String> fallback() { return new GenericEntity<String>(); }
}
public static class MethodGenericDefinitionFailureCase7 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> Set<T> command() { throw new IllegalStateException(); }
private <T> List<T> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase8 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> GenericEntity<Set<T>> command() { throw new IllegalStateException(); }
private <T> GenericEntity<List<T>> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase9 {
@HystrixCommand(fallbackMethod = "fallback")
public <T> GenericEntity<List<Set<T>>> command() { throw new IllegalStateException(); }
private <T> GenericEntity<List<List<T>>> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase10 {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<? super Serializable> command() { throw new IllegalStateException(); }
private GenericEntity<? super Comparable> fallback() { return null; }
}
public static class MethodGenericDefinitionFailureCase11 {
@HystrixCommand(fallbackMethod = "fallback")
public Completable command() { throw new IllegalStateException(); }
private void fallback() { return; }
}
/* ====================================================================== */
/* ===================== GENERIC CLASS DEFINITIONS =====+================ */
/* =========================== SUCCESS CASES ============================ */
public static class ClassGenericDefinitionSuccessCase0<T> {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<T> command() { throw new IllegalStateException(); }
private GenericEntity<T> fallback() { return new GenericEntity<T>(); }
}
public static class ClassGenericDefinitionSuccessCase1<T extends Serializable> {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<T> command() { throw new IllegalStateException(); }
private GenericEntity<T> fallback() { return new GenericEntity<T>(); }
}
/* ====================================================================== */
/* ===================== GENERIC CLASS DEFINITIONS =====+================ */
/* =========================== FAILURE CASES ============================ */
public static class ClassGenericDefinitionFailureCase0<T> {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<T> command() { throw new IllegalStateException(); }
private <T> GenericEntity<T> fallback() { return new GenericEntity<T>(); }
}
public static class ClassGenericDefinitionFailureCase1<T extends Serializable> {
@HystrixCommand(fallbackMethod = "fallback")
public GenericEntity<T> command() { throw new IllegalStateException(); }
private <T extends Serializable> GenericEntity<T> fallback() { return new GenericEntity<T>(); }
}
public static class GenericEntity<T> {
}
private static Object executeCommand(Object proxy) {
try {
Method method = proxy.getClass().getDeclaredMethod("command");
return method.invoke(proxy);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof FallbackDefinitionException) {
throw (FallbackDefinitionException) t;
} else throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}