Our application is written in anit-fragile manner by implementing circuit breaker pattern using Hystrix.
The whole of the application is created using test driven practice but is stuck at the point where we need to implement the circuit breaker strategy by configuring the same on the methods.
Below is the sample configuration used by us -
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "8"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "25"),
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")},
fallbackMethod = "retrieveMapFallback")
Can anyone comment, if there is an available feature or opportunity to test drive it in my Integration Test (which loads the whole of WebApplicationContext, and hence is aware of all configurations available with the application)?
Or if this is not at all possible to be verified in my app context?
Any inputs will be of value.
You can test your Hystrix
circuit breaker configuration.
For instance, take a look at this example application with Spring Boot 1.4
:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.stereotype.Component;
@EnableCircuitBreaker
@SpringBootApplication
public class HystrixDemo {
public static void main(String[] args) {
SpringApplication.run(HystrixDemo.class, args);
}
@Component
static class MyService {
static final String COMMAND_KEY = "MyCommandKey";
private final Outbound outbound;
MyService(Outbound outbound) {
this.outbound = outbound;
}
@HystrixCommand(
commandKey = COMMAND_KEY,
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2016")
})
void process() {
outbound.call();
}
}
interface Outbound {
void call();
}
}
Your configuration tests may look like this one:
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandMetrics;
import com.netflix.hystrix.HystrixCommandProperties;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertTrue;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceCircuitBreakerConfigurationTests {
@Autowired
private HystrixDemo.MyService myService;
@MockBean
private HystrixDemo.Outbound outbound;
@Before
public void setup() {
warmUpCircuitBreaker();
}
@Test
public void shouldHaveCustomTimeout() {
assertTrue(getCircuitBreakerCommandProperties().executionTimeoutInMilliseconds().get() == 2016);
}
private void warmUpCircuitBreaker() {
myService.process();
}
public static HystrixCommandProperties getCircuitBreakerCommandProperties() {
return HystrixCommandMetrics.getInstance(getCommandKey()).getProperties();
}
private static HystrixCommandKey getCommandKey() {
return HystrixCommandKey.Factory.asKey(HystrixDemo.MyService.COMMAND_KEY);
}
}
In addition, if you want to test circuit breaker you can take a look at this test:
import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.willThrow;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyServiceCircuitBreakerTests {
@Autowired
private HystrixDemo.MyService myService;
@MockBean
private HystrixDemo.Outbound outbound;
@Before
public void setup() {
resetHystrix();
warmUpCircuitBreaker();
openCircuitBreakerAfterOneFailingRequest();
}
@Test
public void shouldTripCircuit() throws InterruptedException {
willThrow(new RuntimeException()).given(outbound).call();
HystrixCircuitBreaker circuitBreaker = getCircuitBreaker();
// demonstrates circuit is actually closed
assertFalse(circuitBreaker.isOpen());
assertTrue(circuitBreaker.allowRequest());
try {
myService.process();
fail("unexpected");
} catch (RuntimeException exception) {
waitUntilCircuitBreakerOpens();
assertTrue(circuitBreaker.isOpen());
assertFalse(circuitBreaker.allowRequest());
}
}
private void waitUntilCircuitBreakerOpens() throws InterruptedException {
/* one second is almost sufficient
borrowed from https://github.com/Netflix/Hystrix/blob/v1.5.5/hystrix-core/src/test/java/com/netflix/hystrix/HystrixCircuitBreakerTest.java#L140
*/
Thread.sleep(1000);
}
private void resetHystrix() {
Hystrix.reset();
}
private void warmUpCircuitBreaker() {
myService.process();
}
public static HystrixCircuitBreaker getCircuitBreaker() {
return HystrixCircuitBreaker.Factory.getInstance(getCommandKey());
}
private static HystrixCommandKey getCommandKey() {
return HystrixCommandKey.Factory.asKey(HystrixDemo.MyService.COMMAND_KEY);
}
private void openCircuitBreakerAfterOneFailingRequest() {
ConfigurationManager.getConfigInstance().setProperty("hystrix.command." + HystrixDemo.MyService.COMMAND_KEY + ".circuitBreaker.requestVolumeThreshold", 1);
}
}