Back to Repositories

Testing Exception Handling Patterns in Hystrix Command Configuration in Netflix/Hystrix

This test suite validates the DefaultProperties ignoreExceptions functionality in Netflix’s Hystrix library. It verifies how exceptions are handled and propagated through both default and override scenarios, ensuring proper exception inheritance and fallback behavior in Hystrix commands.

Test Coverage Overview

The test suite provides comprehensive coverage of exception handling patterns in Hystrix commands:

  • Default exception inheritance scenarios
  • Command-level exception override cases
  • Fallback command exception handling
  • Non-ignored exception propagation

Implementation Analysis

The testing approach uses JUnit’s expected exception pattern to validate various exception handling scenarios. It implements an abstract test class with concrete service implementations, allowing for flexible testing of different exception configurations and inheritance patterns.

The tests specifically focus on @DefaultProperties and @HystrixCommand annotations’ interaction for exception handling.

Technical Details

Testing infrastructure includes:

  • JUnit 4 framework
  • Hystrix annotations (@DefaultProperties, @HystrixCommand)
  • Custom exception classes (BadRequestException, SpecificException)
  • Abstract test pattern for service creation

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Systematic exception handling verification
  • Clear test method naming conventions
  • Proper use of @Ignore annotations with issue references
  • Structured test hierarchy with abstract base class
  • Comprehensive documentation of test scenarios

netflix/hystrix

hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultIgnoreExceptionsTest.java

            
package com.netflix.hystrix.contrib.javanica.test.common.error;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

/**
 * Test for {@link DefaultProperties#ignoreExceptions()} feature.
 *
 * <p>
 * Created by dmgcodevil.
 */
public abstract class BasicDefaultIgnoreExceptionsTest {
    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 = BadRequestException.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 = BadRequestException.class)
    public void testFallbackCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
        service.commandWithFallbackOverridesDefaultIgnoreExceptions(BadRequestException.class);
    }

    @DefaultProperties(ignoreExceptions = BadRequestException.class)
    public static class Service {
        @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);
        }
    }
}