Spring, Spring Boot and TestNG Testing Guide - Testing @Configuration

Code cloud address

After Spring introduced the Java Config mechanism, we will use more and more @Configuration to register beans, and Spring Boot uses this mechanism more widely, and the large number of Auto Configuration provided by it greatly simplifies the configuration work. So the question is, how to ensure that @Configuration and Auto Configuration work as expected, and are the beans registered correctly? This chapter gives examples to test the methods of @Configuration and Auto Configuration (because Auto Configuration is also @Configuration, the test methods are the same).

Example 1: Testing @Configuration

Let's start by writing a simple @Configuration:

@Configuration
public class FooConfiguration {

  @Bean
  public Foo foo() {
    return new Foo();
  }

}

Then see if FooConfiguration can correctly register the bean:

public class FooConfigurationTest {

  private AnnotationConfigApplicationContext context;

  @BeforeMethod
  public void init() {
    context = new AnnotationConfigApplicationContext();
  }

  @AfterMethod(alwaysRun = true)
  public void reset() {
    context.close();
  }

  @Test
  public void testFooCreation() {
    context.register(FooConfiguration.class);
    context.refresh();
    assertNotNull(context.getBean(Foo.class));
  }

}

Note the code about Context in the above code:

  1. First, we construct a Context
  2. Then, register FooConfiguration
  3. Then, refresh Context
  4. Finally, close the Context at the end of the test method

If you look at the source code of the @Configuration test in Spring Boot, you will find that it is a bit different from the above code:

public class DataSourceAutoConfigurationTests {

	private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

	@Before
	public void init() {
		EmbeddedDatabaseConnection.override = null;
		EnvironmentTestUtils.addEnvironment(this.context,
				"spring.datasource.initialize:false",
				"spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());
	}

	@After
	public void restore() {
		EmbeddedDatabaseConnection.override = null;
		this.context.close();
	}

This is because both Spring and Spring Boot use JUnit for testing, and the feature of JUnit is that each time a test method is executed, a new test class instance is created, while TestNG is sharing the same test class instance.

Example 2: Testing @Conditional

Spring Framework provides a mechanism to conditionally control @Configuration, that is, @Configuration will only be imported if a certain condition is met, which is @Conditional .

Let's do some tests on @Conditional, first we customize a Condition FooConfiguration :

@Configuration
public class FooConfiguration {

  @Bean
  @Conditional(FooCondition.class)
  public Foo foo() {
    return new Foo();
  }

  public static class FooCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      if (context.getEnvironment() != null) {
        Boolean property = context.getEnvironment().getProperty("foo.create", Boolean.class);
        return Boolean.TRUE.equals(property);
      }
      return false;
    }

  }
}

The Condition determines whether there is any in the Environmentfoo.create=true .

If we want to test this Condition, then we must add relevant properties to the Environment. Here we tested three cases:

  1. no configurationfoo.create=true
  2. configurefoo.create=true
  3. configurefoo.create=false

FooConfigurationTest

public class FooConfigurationTest {

  private AnnotationConfigApplicationContext context;

  @BeforeMethod
  public void init() {
    context = new AnnotationConfigApplicationContext();
  }

  @AfterMethod(alwaysRun = true)
  public void reset() {
    context.close();
  }

  @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
  public void testFooCreatePropertyNull() {
    context.register(FooConfiguration.class);
    context.refresh();
    context.getBean(Foo.class);
  }

  @Test
  public void testFooCreatePropertyTrue() {
    context.getEnvironment().getPropertySources().addLast(
        new MapPropertySource("test", Collections.singletonMap("foo.create", "true"))
    );
    context.register(FooConfiguration.class);
    context.refresh();
    assertNotNull(context.getBean(Foo.class));
  }

  @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
  public void testFooCreatePropertyFalse() {
    context.getEnvironment().getPropertySources().addLast(
        new MapPropertySource("test", Collections.singletonMap("foo.create", "false"))
    );
    context.register(FooConfiguration.class);
    context.refresh();
    assertNotNull(context.getBean(Foo.class));
  }

}

Note that we add properties to the Environment in the following way:

context.getEnvironment().getPropertySources().addLast(
  new MapPropertySource("test", Collections.singletonMap("foo.create", "true"))
);

Therefore, the fundamental test for @Conditional and its corresponding Condition is to give it different conditions to determine whether its behavior is correct. In this example, our Condition is relatively simple, just to determine whether there is a certain property, if it is a complex Condition, The test idea is the same.

Example 3: Testing @ConditionalOnProperty

Spring framework only provides @Conditional, and Spring boot extends this mechanism to provide a richer @ConditionalOn* , here we take @ConditionalOnProperty as an example.

First look at FooConfiguration :

@Configuration
public class FooConfiguration {

  @Bean
  @ConditionalOnProperty(prefix = "foo", name = "create", havingValue = "true")
  public Foo foo() {
    return new Foo();
  }

}

FooConfigurationTest

public class FooConfigurationTest {

  private AnnotationConfigApplicationContext context;

  @BeforeMethod
  public void init() {
    context = new AnnotationConfigApplicationContext();
  }

  @AfterMethod(alwaysRun = true)
  public void reset() {
    context.close();
  }

  @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
  public void testFooCreatePropertyNull() {
    context.register(FooConfiguration.class);
    context.refresh();
    context.getBean(Foo.class);
  }

  @Test
  public void testFooCreatePropertyTrue() {
    EnvironmentTestUtils.addEnvironment(context, "foo.create=true");
    context.register(FooConfiguration.class);
    context.refresh();
    assertNotNull(context.getBean(Foo.class));
  }

  @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
  public void testFooCreatePropertyFalse() {
    EnvironmentTestUtils.addEnvironment(context, "foo.create=false");
    context.register(FooConfiguration.class);
    context.refresh();
    assertNotNull(context.getBean(Foo.class));
  }

}

The logic of this test code is similar to that of Example 2, except that the Condition we wrote ourselves is used in Example 2, and @ConditionalOnProperty provided by Spring Boot is used here.

And using the [EnvironmentTestUtils][javadoc-spring-boot-EnvironmentTestUtils] provided by Spring Boot simplifies the work of adding properties to the Environment:

EnvironmentTestUtils.addEnvironment(context, "foo.create=false");

Example 4: Testing Configuration Properties

Spring Boot also provides type-safe Configuration Properties , the following example how to test it.

BarConfiguration

@Configuration
@EnableConfigurationProperties(BarConfiguration.BarProperties.class)
public class BarConfiguration {

  @Autowired
  private BarProperties barProperties;

  @Bean
  public Bar bar() {
    return new Bar(barProperties.getName());
  }

  @ConfigurationProperties("bar")
  public static class BarProperties {

    private String name;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

}

BarConfigurationTest

public class BarConfigurationTest {

  private AnnotationConfigApplicationContext context;

  @BeforeMethod
  public void init() {
    context = new AnnotationConfigApplicationContext();
  }

  @AfterMethod(alwaysRun = true)
  public void reset() {
    context.close();
  }

  @Test
  public void testBarCreation() {
    EnvironmentTestUtils.addEnvironment(context, "bar.name=test");
    context.register(BarConfiguration.class, PropertyPlaceholderAutoConfiguration.class);
    context.refresh();
    assertEquals(context.getBean(Bar.class).getName(), "test");
  }

}

Note that because we use the Configuration Properties mechanism, we need to register PropertyPlaceholderAutoConfiguration , otherwise BarProperties cannot be injected in BarConfiguration.

Reference documentation

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324535129&siteId=291194637
Recommended