Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to configure context with XML resources

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article focuses on "how to use XML resources to configure context", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to configure context with XML resources.

Context management

Each TestContext provides context management and caching support for the test instance it is responsible for. The test instance does not automatically receive access to the configured ApplicationContext. However, if the test class implements the ApplicationContextAware interface, a reference to ApplicationContext is provided to the test instance. Note that AbstractJUnit4SpringContextTests and AbstractTestNGSpringContextTests implement ApplicationContextAware, so access to ApplicationContext can be provided automatically.

@ Autowired ApplicationContext

As an alternative to implementing the ApplicationContextAware interface, you can inject the application context into the test class through the @ Autowired annotation on the field or setter method, as shown in the following example:

@ SpringJUnitConfigclass MyTest {@ Autowired / / 1 ApplicationContext applicationContext; / / class body...}

Inject ApplicationContext.

Similarly, if the test is configured to load WebApplicationContext, you can inject the Web application context into the test, as follows:

@ SpringJUnitWebConfig / / 1class MyWebAppTest {@ Autowired / / 2 WebApplicationContext wac; / / class body...}

Configure WebApplicationContext

Inject WebApplicationContext

Dependency injection using @ Autowired is provided by DependencyInjectionTestExecutionListener and is configured by default (see dependency injection in the test facility).

Test classes that use the TestContext framework do not need to extend any specific classes or implement specific interfaces to configure their application context. Instead, the configuration is implemented by declaring the @ ContextConfiguration annotation at the class level. If your test class does not explicitly declare the application context resource location or component class, the configured ContextLoader will determine how to load the context from the default location or default configuration class. In addition to the context resource location and component classes, the application context can be configured through the application context initialization program.

The following sections explain how to use the @ ContextConfiguration annotation of Spring to configure test ApplicationContext through XML configuration files, Groovy scripts, component classes (typically @ configuration classes), or context initializers. In addition, you can implement and configure your own custom SmartContextLoader for advanced use cases.

Configure context through XML resources

Configure context through Groovy script

Configure context through component classes

Mixing of XML, Groovy scripts, and component classes

Configure context through context initializer

Context configuration inheritance

Configure context through context configuration

Configure the context by testing the attribute source

Configure context through dynamic attribute sources

Load WebApplicationContext

Context caching

Context level

Configure context through XML resources

To load ApplicationContext for the test using the XML configuration file, annotate the test class with @ ContextConfiguration and configure the locations property with an array containing the resource location of the XML configuration metadata. A simple or relative path, such as context.xml, is considered a classpath resource relative to the package that defines the test class. Paths that begin with a slash are considered absolute classpath locations (for example: / org/example/config.xml). Use the path that represents the resource URL as it is (that is, the path that starts with classpath:, file:, http:, and so on).

@ ExtendWith (SpringExtension.class) / / ApplicationContext loads "/ app-config.xml" and / / "/ test-config.xml" @ ContextConfiguration (locations= {"/ app-config.xml", "/ test-config.xml"}) / / 1class MyTest {/ / class body...} from the root path

Set the locations property to the list of XML files.

@ ContextConfiguration supports aliases for the locations property through the standard Java value property. So, if you don't need to declare other properties in @ ContextConfiguration, you can use the format shown in the following example to omit the declaration of the locations attribute name and declare the resource location.

ExtendWith (SpringExtension.class) @ ContextConfiguration ({"/ app-config.xml", "/ test-config.xml"}) / / 1class MyTest {/ / class body...}

The XML file is specified without using the location property.

If you omit the location and value attributes from the @ ContextConfiguration annotation, the TestContext framework will try to detect the default XML resource location. Specifically, GenericXmlContextLoader and GenericXmlWebContextLoader detect the default location based on the name of the test class. If your class is named com.example.MyTest, GenericXmlContextLoader loads classpath:com/example/MyTest-context.xml into the application context. The following example shows how to do this:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will be loaded from// "classpath:com/example/MyTest-context.xml" @ ContextConfiguration / / 1class MyTest {/ / class body...}

Load configuration from the default location.

Configure context through Groovy script

To load DSL for a test by using a Groovy script that defines DSL using Groovy Bean, you can annotate the test class with @ ContextConfiguration and configure the location or value property with an array containing the location of the Groovy script's resources. The resource lookup semantics of the Groovy script are the same as those described for the XML configuration file.

Activate Groovy scripting support

If there is a Groovy in the classpath, the support for loading ApplicationContext in the Spring TestContext framework using Groovy scripts is automatically enabled.

The following example shows how to specify a Groovy profile:

ExtendWith (SpringExtension.class) / / ApplicationContext will be loaded from "/ AppConfig.groovy" and// "/ TestConfig.groovy" in the root of the classpath@ContextConfiguration ({"/ AppConfig.groovy", "/ TestConfig.Groovy"}) class MyTest {/ / class body...}

If you omit the location and value attributes from the @ ContextConfiguration annotation, the TestContext framework will try to detect the default Groovy script. Specifically, GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader detect the default location based on the name of the test class. If your class is named com.example.MyTest, the Groovy context loader will load the application context from classpath:com/example/MyTestContext.groovy. The following example shows how to use default values:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will be loaded from// "classpath:com/example/MyTestContext.groovy" @ ContextConfiguration / / 1class MyTest {/ / class body...}

Load configuration from the default location.

Declare both the XML configuration and the Groovy script

You can declare both the XML configuration file and the Groovy script using the location or value attribute of @ ContextConfiguration. If the path to the configured resource location ends with .xml, the path is loaded using XmlBeanDefinitionReader. Otherwise, it will be loaded using GroovyBeanDefinitionReader.

The following listing shows how to combine the two in integration testing:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will load the context @ ContextConfiguration ({"/ app-config.xml", "/ TestConfig.groovy"}) class MyTest {/ / class body...} from / / "/ app-config.xml" and "/ TestConfig.groovy"

Configure context through component classes

To load ApplicationContext for a test using a component class (see Java-based container configuration), annotate the test class with @ ContextConfiguration and configure the classes property with an array containing references to the component class. The following example shows how to do this:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will load the context @ ContextConfiguration (classes = {AppConfig.class, TestConfig.class}) / 1class MyTest {/ / class body...} from AppConfig and TestConfig

Specifies the component class.

Component class

The term component class can refer to any of the following:

A class annotated with @ Configuration.

Components (that is, classes annotated with @ Component, @ Service, @ Repository, or other stereotypes).

A JSR-330-compatible class that is annotated with javax.inject annotations.

Any class that contains the @ Bean method.

Any other class that you plan to register as a Spring component (that is, Spring bean in ApplicationContext) may take advantage of the automatic assembly of a single automatic constructor without using Spring annotations.

For more information about the configuration and semantics of component classes, see @ Configuration and @ Bean's javadoc, paying particular attention to the discussion of the @ Bean Lite pattern.

If you omit the classes attribute from the @ ContextConfiguration annotation, the TestContext framework will try to detect the existence of the default configuration class. Specifically, AnnotationConfigContextLoader and AnnotationConfigWebContextLoader detect all statically nested classes of test classes that meet the requirements of the configuration class implementation, as specified in @ Configuration javadoc. Note that the name of the configuration class is arbitrary. In addition, a test class can contain multiple statically nested configuration classes if needed. In the following example, the OrderServiceTest class declares a static nested configuration class named Config, which is automatically used to load ApplicationContext for the test class:

@ SpringJUnitConfig / / 1ramp / ApplicationContext will abscond static cumulant class OrderServiceTest {@ Configuration static class Config {/ / this bean will be injected into the OrderServiceTest class @ Bean OrderService orderService () {OrderService orderService = new OrderServiceImpl (); / / set properties, etc. Return orderService;}} @ Autowired OrderService orderService @ Test void testOrderService () {/ / test the orderService}}

Load configuration information from a nested Config class.

Mixing of XML, Groovy scripts, and component classes

Sometimes you may need to use a mix of XML configuration files, Groovy scripts, and component classes (usually the @ configuration class) to configure ApplicationContext for testing. If you use XML configuration in production, you can decide to use the @ configuration class to configure specific Spring managed components for testing, and vice versa.

In addition, some third-party frameworks (such as Spring Boot) provide best-in-class support to load ApplicationContext from different types of resources (such as XML configuration files, Groovy scripts, and @ configuration classes) at the same time. In the past, the Spring framework did not support this standard deployment. Therefore, most SmartContextLoader implementations provided by the Spring framework in the spring-test module support only one resource type per test context. But that doesn't mean you can't use both at the same time. An exception to the general rule is that GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader support both XML configuration files and Groovy scripts. In addition, third-party frameworks can choose to support location and class declarations through @ ContextConfiguration, and with standard test support in the TestContext framework, you can select the following options.

If you want to use the resource location (such as XML or Groovy) and the @ Configuration class for configuration testing, you must select one as the entry point, and one of them must include or import the other. For example, in XML or Groovy scripts, you can include @ Configuration classes by using component scanning or defining them as normal Spring bean, while in @ Configuration classes, you can use @ ImportResource to import XML configuration files or Groovy scripts. Note that this behavior is semantically equivalent to the way you configure your application in a production environment: in a production configuration, you define a set of XML or Groovy resource locations or a set of @ Configuration classes from which the production ApplicationContext is loaded, but you are still free to include or import other types of configurations.

Configure context through context initializer

To use the context initializer to configure ApplicationContext for your test, annotate the test class with @ ContextConfiguration and configure the initializer properties with an array containing references to the class that implements ApplicationContextInitializer. Then, use the declared context initializer to initialize to the test-loaded ConfigurableApplicationContext. Note that the specific ConfigurableApplicationContext types supported by each declared initializer must be compatible with the ApplicationContext types (usually GenericApplicationContext) created by the SmartContextLoader in use. In addition, the order in which initializers are called depends on whether they implement Spring's Ordered interface or are annotated with Spring's @ Order annotation or the standard @ Priority annotation. The following example shows how to use the initialization program:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will initialize @ ContextConfiguration (classes = TestConfig.class, initializers = TestAppCtxInitializer.class) / / 1class MyTest {/ / class body...} from TestConfig// and through TestAppCtxInitializer

Use the configuration class and initializer to specify the configuration.

You can also completely omit the declaration of the XML configuration file, Groovy script, or component class in @ ContextConfiguration and declare only the ApplicationContextInitializer class, which is then responsible for registering Bean in the context (for example, programmatically loading Bean definitions from XML files) or configuration classes. The following example shows how to do this:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will be initialized by EntireAppInitializer// which presumably registers beans in the context@ContextConfiguration (initializers = EntireAppInitializer.class) / / 1class MyTest {/ / class body...}

Use only the initialization program to specify the configuration.

Reference code: org.liyong.test.annotation.test.spring.ContextInitializerTests

Context configuration inheritance

@ ContextConfiguration supports the boolean inheritLocations and inheritinitialalizer attributes, which indicate whether resource locations or component classes and context initializers declared by the superclass should be inherited. The default value for these two flags is true. This means that the test class will inherit the resource location or component class as well as the context initializer of any superclass declaration. Specifically, attach the resource location or component class of the test class to the resource location declared by the superclass or to the list of annotated classes. Similarly, the initializer for a given test class is added to the initialization assembly defined by the test superclass. Therefore, subclasses can choose to extend resource locations, component classes, or context initializers.

If the inheritLocations or inheritInitializers property in @ ContextConfiguration is set to false, the resource location of the test class or the component class and the context initializer will effectively replace the configuration defined by the superclass, respectively.

In the next example that uses the XML resource location, load the ApplicationContext of ExtendedContext from Base-config.xml and then from Extended-config.xml. Therefore, the Bean defined in extended-config.xml can override (that is, replace) those defined in base-config.xml. The following example shows how one class extends another class and uses its own configuration file and superclass configuration file:

@ ExtendWith (SpringExtension.class) / / ApplicationContext will load "/ base-config.xml" @ ContextConfiguration ("/ base-config.xml") / / 1class BaseTest {/ / class body...} / / ApplicationContext will load "/ base-config.xml" and / / "/ extended-config.xml" @ ContextConfiguration ("/ extended-config.xml") / / 2class ExtendedTest extends BaseTest {/ / class body...} from the classpath root directory

Configuration files defined in the superclass

The configuration file defined in the subclass.

Similarly, in the next example that uses component classes, the ApplicationContext of ExtendedTest is loaded from the BaseConfig and ExtendedConfig classes in that order. Therefore, the Bean defined in ExtendedConfig can override (that is, replace) the Bean defined in BaseConfig. The following example shows how one class extends another class and uses both its own configuration class and the configuration class of the superclass:

/ / ApplicationContext load @ SpringJUnitConfig (BaseConfig.class) / / 1class BaseTest {/ / class body...} / / ApplicationContext from BaseConfig will load @ SpringJUnitConfig (ExtendedConfig.class) / / 2class ExtendedTest extends BaseTest {/ / class body...} from BaseConfig and ExtendedConfig

Configuration classes defined in the superclass

The configuration class defined in the subclass.

In the next example using the context initialization program, initialize the ApplicationContext of ExtendedTest by using BaseInitializer and ExtendedInitializer. Note, however, that the order in which initializers are called depends on whether they implement Spring's Ordered interface or are annotated with Spring's @ Order annotation or the standard @ Priority annotation. The following example shows how one class extends another class and uses its own initializer and superclass initializer:

/ / ApplicationContext will initialize @ SpringJUnitConfig (initializers = BaseInitializer.class) / / 1class BaseTest {/ / class body...} / / ApplicationContext will initialize @ SpringJUnitConfig (initializers = ExtendedInitializer.class) / / 2class ExtendedTest extends BaseTest {/ / class body...} via BaseInitializer// and ExtendedInitializer

The initializer defined in the superclass.

The initializer defined in the subclass.

Use the environment profile for context configuration

The Spring framework provides state-of-the-art support for the concept of environment and configuration files (aka "bean definition profiles"), and integration tests can be configured to activate specific bean definition profiles for various test scenarios. This can be achieved by annotating the test class with @ ActiveProfiles and providing a list of configuration files that should be activated when loading the test's ApplicationContext.

You can use @ ActiveProfiles with any implementation of SmartContextLoader SPI, but older ContextLoader SPI implementations do not support @ ActiveProfiles.

Consider two examples with XML configuration and the @ Configuration class:

ExtendWith (SpringExtension.class) / / ApplicationContext will be loaded from "classpath:/app-config.xml" @ ContextConfiguration ("/ app-config.xml") @ ActiveProfiles ("dev") class TransferServiceTest {@ Autowired TransferService transferService; @ Test void testTransferService () {/ / test the transferService}}

When you run TransferServiceTest, its ApplicationContext is loaded from the app-config.xml configuration file in the root of the classpath. If you examine app-config.xml, you can see that accountRepository bean is dependent on dataSource bean. However, dataSource is not defined as a top-level bean. Instead, dataSource defines it three times: in the production configuration file, in the development configuration file, and in the default configuration file.

By annotating TransferServiceTest with @ ActiveProfiles ("dev"), we instruct the Spring TestContext framework to load ApplicationContext with an activation profile set to {"dev"}. As a result, an embedded database is created, populated with test data, and connected to the accountRepository bean with a reference to the development DataSource. This may be what we want in the integration test.

Sometimes it is useful to assign bean to the default profile. The bean in the default profile is included only if no other profile is specifically activated. You can use it to define the backup bean used in the default state of the application. For example, you can explicitly provide data sources for dev and production, but define the in-memory data source as the default when neither is active.

The following code listing shows how to use the @ Configuration class instead of XML to implement the same configuration and integration tests:

@ Configuration@Profile ("dev") public class StandaloneDataConfig {@ Bean public DataSource dataSource () {return new EmbeddedDatabaseBuilder () .setType (EmbeddedDatabaseType.HSQL) .addScript ("classpath:com/bank/config/sql/schema.sql") .addScript ("classpath:com/bank/config/sql/test-data.sql") .build () } @ Configuration@Profile ("production") public class JndiDataConfig {@ Bean (destroyMethod= ") public DataSource dataSource () throws Exception {Context ctx = new InitialContext (); return (DataSource) ctx.lookup (" java:comp/env/jdbc/datasource ") } @ Configuration@Profile ("default") public class DefaultDataConfig {@ Bean public DataSource dataSource () {return new EmbeddedDatabaseBuilder () .setType (EmbeddedDatabaseType.HSQL) .addScript ("classpath:com/bank/config/sql/schema.sql") .build ();} @ Configurationpublic class TransferServiceConfig {@ Autowired DataSource dataSource; @ Bean public TransferService transferService () {return new DefaultTransferService (accountRepository (), feePolicy ()) } @ Bean public AccountRepository accountRepository () {return new JdbcAccountRepository (dataSource);} @ Bean public FeePolicy feePolicy () {return new ZeroFeePolicy ();}} @ SpringJUnitConfig ({TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class, DefaultDataConfig.class}) @ ActiveProfiles ("dev") class TransferServiceTest {@ Autowired TransferService transferService @ Test void testTransferService () {/ / test the transferService}}

In this variant, we divide the XML configuration into four separate @ Configuration classes:

RansferServiceConfig: get the data source by using @ Autowired for dependency injection.

StandaloneDataConfig: define data sources for embedded databases that are suitable for developers to test.

JndiDataConfig: defines the data source that is retrieved from JNDI in a production environment.

DefaultDataConfig: if no configuration file is active, define a data source for the default embedded database.

As with the XML-based configuration example, we still annotate TransferServiceTest with @ ActiveProfiles ("dev"), but this time we use the @ ContextConfiguration annotation to specify all four configuration classes. The body of the test class itself remains completely the same.

It is common to use a set of configuration files across multiple test classes in a given project. Therefore, to avoid repeated declarations of the @ ActiveProfiles annotation, you can declare @ ActiveProfiles once in the base class, and the subclass automatically inherits the @ ActiveProfiles configuration from the base class. In the following example, the declaration of @ ActiveProfiles (and other annotations) has been moved to the abstract superclass AbstractIntegrationTest:

@ SpringJUnitConfig ({TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class, DefaultDataConfig.class}) @ ActiveProfiles ("dev") abstract class AbstractIntegrationTest {} / / "dev" configuration integration parent class TransferServiceTest extends AbstractIntegrationTest {@ Autowired TransferService transferService; @ Test void testTransferService () {/ / test the transferService}}

@ ActiveProfiles also supports the inherited InheritedProfiles attribute that can be used to disable the activation profile, as shown in the following example:

/ / the "dev" configuration is overwritten by "production" @ ActiveProfiles (profiles = "production", inheritProfiles = false) class ProductionTransferServiceTest extends AbstractIntegrationTest {/ / test body}

In addition, it is sometimes necessary to parse the test's activation configuration file programmatically rather than declaratively, for example, based on:

The current operating system.

Whether to perform tests on the continuous integration build server.

There are some environment variables.

The existence of custom class-level annotations.

Other questions.

To parse the active bean definition configuration file programmatically, you can implement a custom ActiveProfilesResolver and register it using the resolver attribute of @ ActiveProfiles. For more information, see the appropriate javadoc. The following example shows how to implement and register a custom OperatingSystemActiveProfilesResolver:

/ / the "dev" configuration programmatically overrides @ ActiveProfiles (resolver = OperatingSystemActiveProfilesResolver.class, inheritProfiles = false) class TransferServiceTest extends AbstractIntegrationTest {/ / test body} public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {@ Override public String [] resolve (Class testClass) {String profile =...; / / determine the value of profile based on the operating system return new String [] {profile};}} through a custom parser.

Configure the context by testing the attribute source

The Spring framework provides state-of-the-art support for environment concepts with attribute source hierarchies, and you can configure integration tests with test-specific attribute sources. In contrast to the @ PropertySource annotation used on the @ configuration class, you can declare the @ TestPropertySource annotation on the test class to declare the resource location of the test property file or inline property. These test property sources are added to the PropertySources collection of ApplicationContext loaded for annotated integration tests in the environment.

You can use @ TestPropertySource with any implementation of SmartContextLoader SPI, but older ContextLoader SPI implementations do not support @ TestPropertySource.

The implementation of SmartContextLoader can access the merged test property source values through the getPropertySourceLocations () and getPropertySourceProperties () methods in MergedContextConfiguration.

Declare the test property source

You can use the location or value properties of @ TestPropertySource to configure the test properties file.

Supports traditional and XML-based properties file formats, such as classpath:/com/example/test.properties or file:///path/to/file.xml.

Each path is resolved to a Spring resource. A normal path, such as test.properties, is considered a classpath resource relative to the package that defines the test class. Paths that begin with a slash are considered absolute classpath resources (for example: / org/example/test.xml). Load a path that references URL (for example, a path prefixed with classpath:, file:, or http:) by using the specified resource protocol. The use of resource location wildcards (for example, * / .properties) is not allowed. Each location must be accurately evaluated as a .properties or .xml resource.

The following example uses a test properties file:

@ ContextConfiguration@TestPropertySource ("/ test.properties") / / 1class MyIntegrationTests {/ / class body...}

Specifies the properties file with an absolute path.

You can use the properties property of @ TestPropertySource to configure inline properties in the form of key / value pairs, as shown in the following example. All key-value pairs are added to the closed environment as the single test PropertySource with the highest priority.

The syntax supported by key-value pairs is the same as that defined for entries in the Java properties file:

Key=value

Key:value

Key value

The following example sets two inline properties:

ContextConfiguration@TestPropertySource (properties = {"timezone = GMT", "port: 4242"}) / / 1class MyIntegrationTests {/ / class body...}

Set two properties by using two variants of the key-value syntax.

Starting with Spring framework 5.2, @ TestPropertySource can be used as repeatable annotations. This means that you can have multiple declarations of @ TestPropertySource on a single test class, and the locations and properties in the subsequent @ TestPropertySource annotation will override the locations and properties in the previous @ TestPropertySource annotation.

In addition, you can declare multiple combined annotations on a test class, each meta-annotated with @ TestPropertySource, and all of these @ TestPropertySource declarations will contribute to your test property source. Directly rendered @ TestPropertySource annotations always take precedence over meta-rendered @ TestPropertySource annotations. In other words, the locations and properties in the directly existing @ TestPropertySource annotation will override the locations and properties used as meta-annotations in the @ TestPropertySource annotation.

Default properties file detection

If @ TestPropertySource is declared as an empty annotation (that is, there is no explicit value for locations or properties), try to detect the default properties file relative to the class that declares the annotation. For example, if the annotated test class is com.example.MyTest, the corresponding properties file is classpath:com/example/MyTest.properties. If the default value cannot be detected, IllegalStateException is thrown.

Priority order

Test properties take precedence over those defined in the operating system environment, Java system properties, or attribute sources that applications add declaratively or programmatically using @ PropertySource. Therefore, test properties can be used to selectively override properties loaded from system and application property sources. In addition, inline properties take precedence over attributes loaded from the resource location. Note, however, that properties registered with @ DynamicPropertySource have higher priority than those loaded through @ TestPropertySource.

In the next example, the timezone and port properties and any properties defined in / test.properties will override the properties of the same name defined in the system and application property sources. In addition, if the / test.properties file defines entries for the timezone and port attributes, these entries will be overwritten by inline attributes declared with the properties attribute.

ContextConfiguration@TestPropertySource (locations = "/ test.properties", properties = {"timezone = GMT", "port: 4242"}) class MyIntegrationTests {/ / class body...}

Inherit and override test property sources

@ TestPropertySource supports Boolean inheritLocations and inheritProperties attributes, which indicate whether the resource location of the property file and the inline properties declared by the superclass should be inherited. The default value for these two flags is true. This means that the test class will inherit the location and inline properties of any superclass declaration. Specifically, the location and inline properties of the test class are attached to the location and inline properties of the parent class declaration. Therefore, subclasses can choose to extend the location and inline properties. Note that the attribute that appears later hides (that is, overrides) the attribute of the same name that appears earlier. In addition, the precedence rules mentioned earlier also apply to inherited test property sources.

If the InheritLocations or InheritProperties property in @ TestPropertySource is set to false, the location or inline properties are set for the test class, respectively, and the configuration defined by the superclass is effectively replaced.

In the next example, BaseTest's ApplicationContext is loaded by using only base. The properties file is used as the source of test properties. In contrast, ExtendedTest's ApplicationContext is loaded by using base properties and extensions. The property file is used as the test property source location. The following example shows how to use a properties file to define properties in subclasses and their superclasses:

@ TestPropertySource ("base.properties") @ ContextConfigurationclass BaseTest {/ /...} @ TestPropertySource ("extended.properties") @ ContextConfigurationclass ExtendedTest extends BaseTest {/ /...}

In the next example, only the inline key1 property is used to load the ApplicationContext of BaseTest. Instead, use the inline key1 and key2 properties to load the ApplicationContext of ExtendedTest. The following example shows how to define properties in a subclass and its parent class by using inline attributes:

@ TestPropertySource (properties = "key1 = value1") @ ContextConfigurationclass BaseTest {/ /.} @ TestPropertySource (properties = "key2 = value2") @ ContextConfigurationclass ExtendedTest extends BaseTest {/ /.}

Configure context through dynamic attribute sources

Starting with Spring framework 5.2.5, the TestContext framework provides support for dynamic attributes through the @ DynamicPropertySource annotation. This annotation can be used for integration tests that need to add properties with dynamic values to the PropertySources set in the environment of the ApplicationContext loaded for the integration test.

The @ DynamicPropertySource annotation and the infrastructure it supports were originally designed to make properties in Testcontainers-based tests easy to expose to Spring integration tests. However, this feature can also be used for any form of external resource that its life cycle is maintained outside of the ApplicationContext being tested.

In contrast to applying the @ TestPropertySource annotation at the class level, @ DynamicPropertySource must be applied to static methods that accept a single DynamicPropertyRegistry parameter, which is used to add name / value pairs to the environment. The value is dynamic and provided through Supplier, and Supplier is called only when the property is parsed. Typically, a method reference is used to provide a value, as shown in the following example, which uses a Testcontainers project to manage a Redis container outside of Spring ApplicationContext. Through the redis.host and redis.port properties, components in the ApplicationContext being tested can use the IP address and port of the managed Redis container. These properties can be accessed abstractly through Spring's environment, or injected directly into components managed by Spring, such as @ Value ("${redis.host}") and @ Value ("${redis.port}"), respectively.

@ SpringJUnitConfig (/ *... * /) @ Testcontainersclass ExampleIntegrationTests {@ Container static RedisContainer redis = new RedisContainer (); @ DynamicPropertySource static void redisProperties (DynamicPropertyRegistry registry) {registry.add ("redis.host", redis::getContainerIpAddress); registry.add ("redis.port", redis::getMappedPort);} / / tests.}

Priority order

Dynamic attributes take precedence over @ TestPropertySource, the environment of the operating system, Java system properties, or properties loaded in the attribute source that the application adds declaratively or programmatically through @ PropertySource. Therefore, dynamic properties can be used to selectively override properties loaded through @ TestPropertySource, system property sources, and application property sources.

Load WebApplicationContext

To instruct the TestContext framework to load WebApplicationContext instead of standard ApplicationContext, you can annotate their respective test classes with @ WebAppConfiguration.

The presence of @ WebAppConfiguration on the test class indicates that the TestContext framework (TCF) should load WebApplicationContext (WAC) for the integration test. TCF ensures that the MockServletContext is created and provided to the WAC of the test in the background. By default, the basic resource path of your MockServletContext is set to src/main/webapp. This is interpreted as the path relative to the JVM root (usually the path of the project). If you are familiar with the directory structure of Web applications in the Maven project, you know that src/main/webapp is the default location of the WAR root directory. If you need to override this default, you can provide an alternative path to the @ WebAppConfiguration annotation (for example, @ WebAppConfiguration ("src/test/webapp")). If you want to reference the base resource path from the classpath rather than from the file system, you can use the classpath: prefix of Spring.

Note that Spring's test support for WebApplicationContext implementations is comparable to its support for standard ApplicationContext implementations. When testing with WebApplicationContext, you can use @ ContextConfiguration to declare XML configuration files, Groovy scripts, or @ Configuration classes. You are also free to use any other test annotations, such as @ ActiveProfiles, @ Testexecutionlistener, @ Sql, @ Rollback, and others.

The remaining examples in this section show some different configuration options for loading WebApplicationContext. The following example shows the TestContext framework's support for configuration conventions:

ExtendWith (SpringExtension.class) / / defaults to "file:src/main/webapp" @ WebAppConfiguration// detects "WacTests-context.xml" in the same package// or static nested @ Configuration classes@ContextConfigurationclass WacTests {/ /...}

If you annotate the test class with @ WebAppConfiguration without specifying the resource base path, the resource path actually defaults to file:src/main/webapp. Similarly, if the resource location, component class, or context initializer is not specified when @ ContextConfiguration is declared, Spring attempts to use the convention (that is, the WacTests-context.xml is in the same package as the WacTests class or the statically nested @ configuration class).

The following example shows how to use @ WebAppConfiguration to explicitly declare the resource base path and @ ContextConfiguration to explicitly declare the XML resource location:

ExtendWith (SpringExtension.class) / / file system resource@WebAppConfiguration ("webapp") / / classpath resource@ContextConfiguration ("/ spring/test-servlet-config.xml") class WacTests {/ /...}

The important point to note here is that the semantics of the paths with these two annotations are different. By default, the @ WebAppConfiguration resource path is based on the file system, while the @ ContextConfiguration resource location is based on the classpath. The following example shows that we can override the default resource semantics of two annotations by specifying a Spring resource prefix:

ExtendWith (SpringExtension.class) / / classpath resource@WebAppConfiguration ("classpath:test-web-resources") / / file system resource@ContextConfiguration ("file:src/main/webapp/WEB-INF/servlet-config.xml") class WacTests {/ /...}

Compare the comments in this example with the previous example.

Working with Web Mock

To provide comprehensive Web testing support, the TestContext framework has ServletTestExecutionListener enabled by default. When testing against WebApplicationContext, this TestExecutionListener uses Spring Web's RequestContextHolder to set the default thread-local state before each test method, and creates MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest based on the basic resource path configured through @ WebAppConfiguration.

ServletTestExecutionListener also ensures that MockHttpServletResponse and ServletWebRequest can be injected into the test instance, and once the test is complete, it clears the thread-local state.

Once the WebApplicationContext is loaded for the test, you may find that you need to interact with the Web simulation, for example, to set the test fixture or execute assertions after calling the Web component. The following example shows which simulations can be automatically assembled into your test instance. Note that both WebApplicationContext and MockServletContext are cached in the test suite, while other simulations are managed by ServletTestExecutionListener for each test method.

@ SpringJUnitWebConfigclass WacTests {@ Autowired WebApplicationContext wac; / / cached @ Autowired MockServletContext servletContext; / / cached @ Autowired MockHttpSession session; @ Autowired MockHttpServletRequest request; @ Autowired MockHttpServletResponse response; @ Autowired ServletWebRequest webRequest; / /...}

Context caching

Once the TestContext framework loads ApplicationContext (or WebApplicationContext) for the test, the context is cached and reused for all subsequent tests that declare the same unique context configuration in the same test suite. To understand how caching works, it is important to understand the meaning of unique and test suites.

The ApplicationContext can be uniquely identified by a combination of configuration parameters used to load it. Therefore, a unique combination of configuration parameters is used to generate a key under which the context is cached. The TestContext framework uses the following configuration parameters to build context cache keys:

Locations (from @ ContextConfiguration)

Classes (from @ ContextConfiguration)

ContextInitializerClasses (from @ ContextConfiguration)

ContextCustomizers (from ContextCustomizerFactory) includes the @ DynamicPropertySource method, as well as various features in Spring Boot test support, such as @ MockBean and @ SpyBean.

ContextLoader (from @ ContextConfiguration)

Parent (from @ ContextHierarchy)

ActiveProfiles (from @ ActiveProfiles)

PropertySourceLocations (from @ TestPropertySource)

PropertySourceProperties (from @ TestPropertySource)

ResourceBasePath (from @ WebAppConfiguration)

For example, if TestClassA specifies {"app-config.xml", "test-config.xml"} for the location (or value) attribute of @ ContextConfiguration, the TestContext framework will load the corresponding ApplicationContext and store it in the static context cache under key based only on those locations. Therefore, if the TestClassB also defines {"app-config.xml", "test-config.xml"} for its location (explicitly or implicitly by inheritance), but does not define @ WebAppConfiguration, different ContextLoader, different activation profiles, different context initializers, different test property sources, or different parent contexts, the two test classes will share the same ApplicationContext. This means that (per test suite) the setup cost of loading the application context only needs to be loaded once, and the subsequent test execution is much faster.

Test suites and branch processes

The Spring TestContext framework stores the application context in a static cache. This means that the context is actually stored in static variables. In other words, if the test is executed in a separate process, the static cache is cleared between each test execution, effectively disabling the caching mechanism.

To benefit from the caching mechanism, all tests must be run in the same process or test suite. This can be achieved by executing all the tests in groups in IDE. Similarly, when performing tests using a build framework such as Ant, Maven, or Gradle, it is important to ensure that the build framework does not derive between tests (fork multiple processes). For example, if you set the forkMode of the Maven Surefire plug-in to always or pertest, the TestContext framework will not be able to cache the application context between test classes, so the build process will run much slower.

The size of the context cache is limited to the default maximum of 32. As soon as the maximum size is reached, the least recently used (LRU) eviction policy is used to expel and close the old context. You can configure the maximum size from the command line or build script by setting the JVM system property called spring.test.context.cache.maxSize. Alternatively, you can use SpringProperties API to set the same properties programmatically.

Because loading a large number of application contexts in a given test suite can cause the suite to take an unnecessary long time to execute, it is often useful to know exactly how many contexts have been loaded and cached. To view statistics for the underlying context cache, you can set the log level of the org.springframework.test.context.cache logging category to DEBUG.

In unlikely cases, tests break the application context and need to be reloaded (for example, by modifying the bean definition or the state of the application object), you can annotate the test class or test method with @ DirtiesContext (see the discussion of @ DirtiesContext in @ DirtiesContext). This instructs Spring to remove the context from the cache and rebuild the application context before running the next test that requires the same application context. Note that DirtiesContextBeforeModesTestExecutionListener and DirtiesContextTestExecutionListener enabled by default provide support for the @ DirtiesContext annotation.

Context level

When writing integration tests that depend on the loaded Spring ApplicationContext, it is usually sufficient to test against a single context. However, sometimes it is useful or even necessary to test the hierarchy of ApplicationContext instances. For example, if you are developing a Spring MVC Web application, the root WebApplicationContext is usually loaded by Spring's ContextLoaderListener and the child WebApplicationContext is loaded by Spring's DispatcherServlet. This results in a parent-child context hierarchy in which shared components and infrastructure configurations are declared in the root context and used by web-specific components in the child context. Another use case can be found in the Spring Batch application, where you usually have a parent context that provides the configuration for the shared batch infrastructure, while the child context provides the configuration for specific batch jobs.

You can write integration tests that use the context hierarchy by using the @ ContextHierarchy annotation to declare the context configuration on a single test class or in the test class hierarchy. If you declare a context hierarchy on multiple classes in the test class hierarchy, you can also merge or override the context configuration at a specific naming level in the context hierarchy. When merging configurations at a given level in the hierarchy, the configuration resource types (that is, XML configuration files or component classes) must be consistent. Otherwise, it is perfectly acceptable to have different levels in a context hierarchy configured with different resource types.

The remaining JUnit Jupiter-based examples in this section show common configuration scenarios that require integration testing using a context hierarchy.

A single test class with a context hierarchy

ControllerIntegrationTests represents a typical integration test scenario for a Spring MVC Web application by declaring a context hierarchy that consists of two levels, one for the root WebApplicationContext (loaded using the TestAppConfig @ configuration class) and one for the scheduler Servlet WebApplicationContext (loaded using the WebConfig @ configuration class). The WebApplicationContext that is automatically assembled into the test instance is the WebApplicationContext for the subcontext (that is, the lowest context in the hierarchy).

The following listing shows this configuration scenario:

@ ExtendWith (SpringExtension.class) @ WebAppConfiguration@ContextHierarchy ({@ ContextConfiguration (classes = TestAppConfig.class), @ ContextConfiguration (classes = WebConfig.class)}) class ControllerIntegrationTests {@ Autowired WebApplicationContext wac; / /.}

Reference code: org.liyong.test.annotation.test.spring.ControllerIntegrationTests

Class hierarchy with implicit parent context

The test class in this example defines the context hierarchy in the test class hierarchy. AbstractWebTests declares the configuration of the root WebApplicationContext in the Spring-driven Web application. Note, however, that AbstractWebTests does not declare @ ContextHierarchy. Therefore, subclasses of AbstractWebTests can choose to participate in the context hierarchy or follow the standard semantics of @ ContextConfiguration. Both SoapWebServiceTests and RestWebServiceTests extend AbstractWebTests and define the context hierarchy using @ ContextHierarchy. As a result, three application contexts are loaded (one for each @ ContextConfiguration declaration), and the application context loaded based on the configuration in AbstractWebTests is set to the parent context of each context loaded by a specific subclass.

@ ExtendWith (SpringExtension.class) @ WebAppConfiguration@ContextConfiguration ("file:src/main/webapp/WEB-INF/applicationContext.xml") public abstract class AbstractWebTests {} @ ContextHierarchy (@ ContextConfiguration ("/ spring/soap-ws-config.xml") public class SoapWebServiceTests extends AbstractWebTests {} @ ContextHierarchy (@ ContextConfiguration ("/ spring/rest-ws-config.xml")) public class RestWebServiceTests extends AbstractWebTests {}

Reference code: org.liyong.test.annotation.test.spring.RestWebServiceTests

Class hierarchy for merging context hierarchy configuration

The class in this example shows the purpose of using named hierarchy levels and merging configurations at specific levels in the context hierarchy. BaseTests defines two levels in the hierarchy, parent and child. ExtendedTests extends BaseTests and instructs the Spring TestContext framework to merge context configuration at the sub-hierarchy level by ensuring that the names declared in the name attribute of @ ContextConfiguration are child elements. The result is that three application contexts are loaded: one for / app-config.xml, one for / user-config.xml, and one for {/ user-config.xml,/order-config.xml}. As in the previous example, the application context loaded from / app-config.xml is set to the parent context (merge configuration files) of the context loaded from / user-config.xml and {"/ user-config.xml", "/ order-config.xml"}. The following listing shows this configuration scenario:

@ ExtendWith (SpringExtension.class) @ ContextHierarchy ({@ ContextConfiguration (name = "parent", locations = "/ app-config.xml"), @ ContextConfiguration (name = "child", locations = "/ user-config.xml")}) class BaseTests {} @ ContextHierarchy (@ ContextConfiguration (name = "child", locations = "/ order-config.xml")) class ExtendedTests extends BaseTests {}

Reference code: org.liyong.test.annotation.test.spring.ExtendedTests

Class hierarchy with overridden context hierarchy configuration

Contrary to the previous example, this example demonstrates how to override the configuration of a given naming level in the context hierarchy by setting the InheritLocations flag in @ ContextConfiguration to false. Therefore, the application context of ExtendedTests is loaded only from / test-user-config.xml, and its parent is set to the context loaded from / app-config.xml. The following listing shows this configuration scenario:

@ ExtendWith (SpringExtension.class) @ ContextHierarchy ({@ ContextConfiguration (name = "parent", locations = "/ app-config.xml"), @ ContextConfiguration (name = "child", locations = "/ user-config.xml")}) class BaseTests {} @ ContextHierarchy (@ ContextConfiguration (name = "child", locations = "/ test-user-config.xml", inheritLocations = false) class ExtendedTests extends BaseTests {}

Clear the context in the context hierarchy

If you use @ DirtiesContext in a test whose context is configured as part of the context hierarchy, you can use the hierarchyMode flag to control how the context cache is cleared. For more details, see the @ DirtiesContext and @ DirtiesContext javadoc discussion in Spring Testing Annotations.

Reference code: org.liyong.test.annotation.test.spring.ExtendedTests1

3.5.6 dependency injection of testing device

When using DependencyInjectionTestExecutionListener (the default configuration), the dependencies of the test instance are injected from bean in the context of configuring the application with @ ContextConfiguration or related annotations. You can use setter injection, field injection, or both, depending on which annotations you choose and whether or not you put them on the setter method or field. If you are using JUnit Jupiter, you can also choose to use constructor injection (see dependency injection with SpringExtension). To be consistent with Spring's annotation-based injection support, you can also use the @ Autowired annotation in Spring or the @ Inject annotation in JSR-330 for field injection and setter injection.

For testing frameworks other than JUnit Jupiter, the TestContext framework does not participate in instantiation of test classes. Therefore, using @ Autowired or @ Inject for the constructor is not valid for the test class.

Although the use of field injection is discouraged in production code, field injection is actually natural in test code. The reason is that you will never instantiate the test class directly. Therefore, there is no need to call the public constructor or the setter method on the test class.

Because @ Autowired is used to perform automatic assembly by type, if you have multiple Bean definitions of the same type, you will not be able to rely on this method for those specific Bean. In this case, you can use @ Autowired with @ Qualifier. You can also choose to use @ Inject with @ Named. Or, if your test class has access to its ApplicationContext, you can perform an explicit lookup by using (for example) a call to applicationContext.getBean ("titleRepository", TitleRepository.class).

If you do not want dependency injection to be applied to the test instance, do not use @ Autowired or @ Inject annotation fields or setter methods. Alternatively, you can disable dependency injection by explicitly configuring your class with @ TestExecutionListeners and ignoring DependencyInjectionTestExecutionListener.class from the listener list.

Consider the scenario of testing the HibernateTitleRepository class, as described in the target section. The next two code listings demonstrate the use of @ Autowired on fields and setter methods. The application context configuration is shown after all sample code listings.

The dependency injection behavior in the following code listing is not specific to JUnit Jupiter. The same DI technology can be used in conjunction with any supported testing framework.

The following example calls a static assertion method, such as assertNotNull (), but does not add Assertions before the declaration. In this case, it is assumed that the method was imported correctly through the import static declaration not shown in the example.

The first code listing shows the JUnit Jupiter-based implementation of the test class that uses @ Autowired for field injection:

@ ExtendWith (SpringExtension.class) / / specifies the Spring configuration to load for this test fixture@ContextConfiguration ("repository-config.xml") class HibernateTitleRepositoryTests {/ / this instance will be dependency injected by type @ Autowired HibernateTitleRepository titleRepository; @ Test void findById () {Title title = titleRepository.findById (new Long (10)); assertNotNull (title);}}

Alternatively, you can configure the class to use @ Autowired for setter injection, as shown below:

ExtendWith (SpringExtension.class) / / specifies the Spring configuration to load for this test fixture@ContextConfiguration ("repository-config.xml") class HibernateTitleRepositoryTests {/ / this instance will be dependency injected by type HibernateTitleRepository titleRepository; @ Autowired void setTitleRepository (HibernateTitleRepository titleRepository) {this.titleRepository = titleRepository;} @ Test void findById () {Title title = titleRepository.findById (new Long (10)); assertNotNull (title);}}

The previous code listing uses the same XML context file (that is, repository-config.xml) referenced by the @ ContextConfiguration annotation. This configuration is shown below:

If you extend from the test base class provided by Spring, which happens to use @ Autowired on one of its setter methods, you may have defined multiple affected types of Bean (for example, multiple DataSource Bean) in the application context. In this case, you can override the setter method and use the @ Qualifier annotation to indicate a specific target bean, as shown below (but be sure to delegate to the override method in the superclass as well):

/ /... @ Autowired @ Override public void setDataSource (@ Qualifier ("myDataSource") DataSource dataSource) {super.setDataSource (dataSource);} / /.

The specified qualifier value indicates the specific DataSource Bean to be injected, narrowing the type matching to a specific Bean. Its value matches the declaration in the corresponding definition. The Bean name is used as a fallback qualifier value, so you can also effectively point to a specific Bean in that name (as previously shown, assuming myDataSource is Bean ID).

At this point, I believe you have a deeper understanding of "how to configure context with XML resources". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report