Back to Repositories

Testing Nacos Config Data Location Resolution in Spring Cloud Alibaba

This test suite validates the functionality of NacosConfigDataLocationResolver, focusing on configuration data location resolution in Spring Cloud Alibaba’s Nacos implementation. It ensures proper handling of Nacos configuration properties and URI parsing for Spring Cloud applications.

Test Coverage Overview

The test suite provides comprehensive coverage of the NacosConfigDataLocationResolver functionality:
  • Configuration prefix validation and resolution
  • Profile-specific configuration handling
  • URI parsing and validation
  • Property override mechanisms
  • Bootstrap context integration

Implementation Analysis

The testing approach utilizes JUnit 5 with Mockito for dependency isolation. It implements systematic verification of configuration resolution scenarios, including edge cases and property inheritance patterns. The tests leverage Spring’s MockEnvironment for environment simulation and configuration testing.

Technical Details

Testing tools and configuration:
  • JUnit Jupiter for test execution
  • Mockito for mock object creation
  • AssertJ for fluent assertions
  • Spring’s MockEnvironment for environment simulation
  • Custom test fixtures for Nacos configuration

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Proper test isolation using @BeforeEach setup
  • Comprehensive error case coverage
  • Clear test method naming conventions
  • Effective use of mocking frameworks
  • Thorough validation of configuration scenarios

alibaba/spring-cloud-alibaba

spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/test/java/com.alibaba.cloud.nacos/configdata/NacosConfigDataLocationResolverTest.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.nacos.configdata;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.cloud.nacos.NacosPropertiesPrefixer;
import com.alibaba.nacos.api.config.ConfigService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;

import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.mock.env.MockEnvironment;

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

/**
 * NacosConfigDataLocationResolver Tester.
 *
 * @author freeman
 */
public class NacosConfigDataLocationResolverTest {

	private NacosConfigDataLocationResolver resolver;

	private ConfigDataLocationResolverContext context = Mockito.mock(
			ConfigDataLocationResolverContext.class);

	private MockEnvironment environment;

	private Binder environmentBinder;

	private ConfigurableBootstrapContext bootstrapContext = Mockito.mock(
			ConfigurableBootstrapContext.class);

	@BeforeEach
	void setup() {
		this.environment = new MockEnvironment();
		this.environmentBinder = Binder.get(this.environment);
		this.resolver = new NacosConfigDataLocationResolver(new DeferredLogs());
		Mockito.when(bootstrapContext.isRegistered(ArgumentMatchers.eq(ConfigService.class))).thenReturn(true);
		Mockito.when(context.getBinder()).thenReturn(environmentBinder);
		Mockito.when(context.getBootstrapContext()).thenReturn(bootstrapContext);
	}

	@Test
	void testIsResolvable_givenIncorrectPrefix_thenReturnFalse() {
		assertThat(
				this.resolver.isResolvable(this.context, ConfigDataLocation.of("test:")))
						.isFalse();
	}

	@Test
	void testIsResolvable_givenCorrectPrefix_thenReturnTure() {
		assertThat(
				this.resolver.isResolvable(this.context, ConfigDataLocation.of("nacos:")))
						.isTrue();
		assertThat(this.resolver.isResolvable(this.context,
				ConfigDataLocation.of("optional:nacos:"))).isTrue();
	}

	@Test
	void testIsResolvable_givenDisable_thenReturnFalse() {

		String prefix = NacosPropertiesPrefixer.getPrefix(environment);

		this.environment.setProperty(prefix + ".config.enabled", "false");
		assertThat(
				this.resolver.isResolvable(this.context, ConfigDataLocation.of("nacos:")))
						.isFalse();
	}

	@Test
	void testResolveProfileSpecific_givenNothing_thenReturnDefaultProfile() {
		NacosConfigDataResource resource = testResolveProfileSpecific();
		assertThat(resource.getProfiles()).isEqualTo("default");
	}

	@Test
	void testStartWithASlashIsOK() {
		String locationUri = "nacos:/app";
		List<NacosConfigDataResource> resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		assertThat(resources.get(0).getConfig().getDataId()).isEqualTo("app");

		locationUri = "nacos:app";
		resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		assertThat(resources.get(0).getConfig().getDataId()).isEqualTo("app");
	}

	@Test
	void testDataIdMustBeSpecified() {
		String locationUri = "nacos:";
		Assertions.assertThatThrownBy(() -> testUri(locationUri))
				.hasMessage("dataId must be specified");
	}

	@Test
	void testInvalidDataId() {
		String locationUri = "nacos:test/test.yml";
		Assertions.assertThatThrownBy(() -> testUri(locationUri)).hasMessage("illegal dataId");
	}

	@Test
	void whenCustomizeSuffix_thenOverrideDefault() {
		String locationUri = "nacos:app";
		List<NacosConfigDataResource> resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		assertThat(resources.get(0).getConfig().getDataId()).isEqualTo("app");
		assertThat(resources.get(0).getConfig().getSuffix()).isEqualTo("properties");

		environment.setProperty("spring.nacos.config.file-extension", "yml");
		locationUri = "nacos:app";
		resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		assertThat(resources.get(0).getConfig().getDataId()).isEqualTo("app");
		assertThat(resources.get(0).getConfig().getSuffix()).isEqualTo("yml");

		locationUri = "nacos:app.json";
		resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		assertThat(resources.get(0).getConfig().getDataId()).isEqualTo("app.json");
		assertThat(resources.get(0).getConfig().getSuffix()).isEqualTo("json");
	}

	@Test
	void testUrisInLocationShouldOverridesProperty() {
		environment.setProperty("spring.nacos.config.group", "default");
		environment.setProperty("spring.nacos.config.refreshEnabled", "true");
		String locationUri = "nacos:test.yml?group=not_default&refreshEnabled=false";
		List<NacosConfigDataResource> resources = testUri(locationUri);
		Assertions.assertThat(resources).hasSize(1);
		NacosConfigDataResource resource = resources.get(0);
		assertThat(resource.getConfig().getGroup()).isEqualTo("not_default");
		assertThat(resource.getConfig().getSuffix()).isEqualTo("yml");
		assertThat(resource.getConfig().isRefreshEnabled()).isFalse();
		assertThat(resource.getConfig().getDataId()).isEqualTo("test.yml");
	}

	@Test
	void testSetCommonPropertiesIsOK() {
		environment.setProperty("spring.nacos.username", "root");
		environment.setProperty("spring.nacos.password", "root");
		environment.setProperty("spring.nacos.server-addr", "127.0.0.1:8888");
		String locationUri = "nacos:test.yml";
		List<NacosConfigDataResource> resources = testUri(locationUri);

		Assertions.assertThat(resources).hasSize(1);
		NacosConfigDataResource resource = resources.get(0);
		assertThat(resource.getProperties().getUsername()).isEqualTo("root");
		assertThat(resource.getProperties().getPassword()).isEqualTo("root");
		assertThat(resource.getProperties().getServerAddr()).isEqualTo("127.0.0.1:8888");
	}

	@Test
	void testCommonPropertiesHasLowerPriority() {
		environment.setProperty("spring.nacos.username", "root");
		environment.setProperty("spring.nacos.password", "root");
		environment.setProperty("spring.nacos.config.password", "not_root");
		environment.setProperty("spring.nacos.server-addr", "127.0.0.1:8888");
		environment.setProperty("spring.nacos.config.server-addr",
				"127.0.0.1:9999");
		String locationUri = "nacos:test.yml";
		List<NacosConfigDataResource> resources = testUri(locationUri);

		Assertions.assertThat(resources).hasSize(1);
		NacosConfigDataResource resource = resources.get(0);
		assertThat(resource.getProperties().getUsername()).isEqualTo("root");
		assertThat(resource.getProperties().getPassword()).isEqualTo("not_root");
		assertThat(resource.getProperties().getServerAddr()).isEqualTo("127.0.0.1:9999");
	}

	private List<NacosConfigDataResource> testUri(String locationUri,
			String... activeProfiles) {
		Profiles profiles = Mockito.mock(Profiles.class);
		Mockito.when(profiles.getActive()).thenReturn(Arrays.asList(activeProfiles));
		return this.resolver.resolveProfileSpecific(context,
				ConfigDataLocation.of(locationUri), profiles);
	}

	@Test
	void whenNoneInBootstrapContext_thenCreateNewConfigClientProperties() {
		Mockito.when(bootstrapContext.isRegistered(ArgumentMatchers.eq(NacosConfigProperties.class)))
				.thenReturn(false);
		Mockito.when(bootstrapContext.get(ArgumentMatchers.eq(NacosConfigProperties.class)))
				.thenReturn(new NacosConfigProperties());
		List<NacosConfigDataResource> resources = this.resolver.resolveProfileSpecific(
				context, ConfigDataLocation.of("nacos:test.yml"), Mockito.mock(Profiles.class));
		Assertions.assertThat(resources).hasSize(1);
		Mockito.verify(bootstrapContext, Mockito.times(0)).get(ArgumentMatchers.eq(NacosConfigProperties.class));
		NacosConfigDataResource resource = resources.get(0);
		assertThat(resource.getConfig().getGroup()).isEqualTo("DEFAULT_GROUP");
		assertThat(resource.getConfig().getDataId()).isEqualTo("test.yml");
	}

	private NacosConfigDataResource testResolveProfileSpecific() {
		return testResolveProfileSpecific("default");
	}

	private NacosConfigDataResource testResolveProfileSpecific(String activeProfile) {
		Profiles profiles = Mockito.mock(Profiles.class);
		if (activeProfile != null) {
			Mockito.when(profiles.getActive())
					.thenReturn(Collections.singletonList(activeProfile));
			Mockito.when(profiles.getAccepted())
					.thenReturn(Collections.singletonList(activeProfile));
		}

		List<NacosConfigDataResource> resources = this.resolver.resolveProfileSpecific(
				context, ConfigDataLocation.of("nacos:test.yml"), profiles);
		Assertions.assertThat(resources).hasSize(1);
		return resources.get(0);
	}

}