Back to Repositories

Testing Sentinel Feign Client Context Isolation in Spring Cloud Alibaba

This test suite validates the integration of Sentinel with Feign clients in Spring Cloud Alibaba, focusing on context ID handling and fallback functionality. It ensures proper isolation and behavior of multiple Feign clients with different context IDs when Sentinel circuit breaking is enabled.

Test Coverage Overview

The test suite provides comprehensive coverage of Sentinel-enabled Feign client functionality:
  • Verification of distinct context IDs for multiple Feign clients
  • Testing of fallback implementation for service failures
  • Validation of object equality and hashCode contracts
  • Integration testing of Sentinel circuit breaker configuration

Implementation Analysis

The testing approach utilizes Spring Boot test framework with JUnit4 integration:
  • Uses @SpringBootTest for full context loading
  • Implements multiple Feign client interfaces with different context IDs
  • Validates both direct fallback and fallback factory patterns
  • Employs assertion-based verification using AssertJ

Technical Details

Key technical components include:
  • Spring Boot Test framework
  • JUnit 4 test runner
  • Sentinel Feign AutoConfiguration
  • Custom FeignConfiguration for fallback handling
  • Mock service endpoints with path and query parameter variations

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Clear separation of test configuration from test cases
  • Comprehensive verification of both positive and negative scenarios
  • Proper usage of Spring Boot test annotations and configuration
  • Well-structured test organization with distinct service interfaces

alibaba/spring-cloud-alibaba

spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-sentinel/src/test/java/com/alibaba/cloud/sentinel/ContextIdSentinelFeignTests.java

            
/*
 * Copyright 2013-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.cloud.sentinel;

import com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Add this unit test to verify https://github.com/alibaba/spring-cloud-alibaba/pull/838.
 *
 * @author <a href="mailto:[email protected]">Jim</a>
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { ContextIdSentinelFeignTests.TestConfig.class },
		properties = { "feign.sentinel.enabled=true" })
public class ContextIdSentinelFeignTests {

	@Autowired
	private EchoService echoService;

	@Autowired
	private FooService fooService;

	@Test
	public void testFeignClient() {
		assertThat(echoService.echo("test")).isEqualTo("echo fallback");
		assertThat(fooService.echo("test")).isEqualTo("foo fallback");
		assertThat(fooService.toString()).isNotEqualTo(echoService.toString());
		assertThat(fooService.hashCode()).isNotEqualTo(echoService.hashCode());
		assertThat(echoService.equals(fooService)).isEqualTo(Boolean.FALSE);
	}

	@Configuration
	@EnableAutoConfiguration
	@ImportAutoConfiguration({ SentinelFeignAutoConfiguration.class })
	@EnableFeignClients
	public static class TestConfig {

	}

	@FeignClient(contextId = "echoService", name = "service-provider",
			fallback = EchoServiceFallback.class,
			configuration = FeignConfiguration.class)
	public interface EchoService {

		@GetMapping("/echo/{str}")
		String echo(@PathVariable("str") String str);

	}

	@FeignClient(contextId = "fooService", value = "foo-service",
			fallbackFactory = CustomFallbackFactory.class,
			configuration = FeignConfiguration.class)
	public interface FooService {

		@RequestMapping(path = "echo/{str}")
		String echo(@RequestParam("str") String param);

	}

	public static class FeignConfiguration {

		@Bean
		public EchoServiceFallback echoServiceFallback() {
			return new EchoServiceFallback();
		}

		@Bean
		public CustomFallbackFactory customFallbackFactory() {
			return new CustomFallbackFactory();
		}

	}

	public static class EchoServiceFallback implements EchoService {

		@Override
		public String echo(@RequestParam("str") String param) {
			return "echo fallback";
		}

	}

	public static class FooServiceFallback implements FooService {

		@Override
		public String echo(@RequestParam("str") String param) {
			return "foo fallback";
		}

	}

	public static class CustomFallbackFactory
			implements FallbackFactory<FooService> {

		private FooService fooService = new FooServiceFallback();

		@Override
		public FooService create(Throwable throwable) {
			return fooService;
		}

	}

}