In order to allow developers to execute TestNG tests in Eclipse IDE
without installing the TestNG plugin for Eclipse, this commit introduces
a JUnit Platform @Suite class that can be executed within the IDE.
See gh-27407
When not excluded, TestNG will pick up nested TestCase classes and run
them.
This commit therefore filters out `*TestCase` test classes from the
build since these are not intended to be executed with the build.
See gh-27406
Prior to this commit, we had configured separate test tasks for JUnit
and TestNG. In addition, we configured a standard `test` task that
depended on the `junit` and `testNG` tasks, and we had an additional
`aggregateTestReports` task that aggregated the reports from the JUnit
and TestNG test tasks.
Thanks to the introduction of the "TestNG Engine for the JUnit
Platform", this commit simplifies our Gradle build in the spring-test
module by running JUnit 4, JUnit Jupiter, and TestNG tests on the JUnit
Platform in a single Gradle `test` task.
See gh-27406
This commit updates the defaultResponseCharacterEncoding() `default`
method in ConfigurableMockMvcBuilder so that it throws an
UnsupportedOperationException instead of silently ignoring the user's
request to set the default response character encoding.
Note, however, that AbstractMockMvcBuilder already overrides the
default method with a concrete implementation which is used by default
in MockMvc.
See gh-27230
It happens very often that WebTestClient is used in heavyweight
integration tests, and it's a hindrance to developer productivity to
fix one failed assertion after another. Soft assertions help a lot by
checking all conditions at once even if one of them fails.
This commit introduces a new expectAllSoftly(..) method in
WebTestClient to address this issue.
client.get().uri("/hello")
.exchange()
.expectAllSoftly(
spec -> spec.expectStatus().isOk(),
spec -> spec.expectBody(String.class).isEqualTo("Hello, World")
);
Closes gh-26969
It happens very often that MockMvc is used in heavyweight integration
tests. It's no use to waste time to check if another condition has been
fixed or not. Soft assertions help a lot by checking all conditions at
once even if one of them fails.
See gh-26917
Co-authored-by: Sach Nguyen <sachnbbkhn@gmail.com>
Prior to this commit, if the user tested file upload support with
HtmlUnit and MockMvc by invoking HtmlFileInput.setData() instead of
HtmlFileInput.setFiles(), the in-memory file data was simply ignored.
This commit addresses this issue by creating a MockPart from the
in-memory data in HtmlUnitRequestBuilder.
Closes gh-27199
To improve the developer experience and avoid the use of String
literals, this commit provides overloaded support via Charset for
character encoding in MockHttpServletRequestBuilder and
ContentResultMatchers.
Closes gh-27231
Commit e4b9b1fadb introduced support for setting the default character
encoding in MockHttpServletResponse.
This commit introduces support for configuring the default character
encoding in the underlying MockHttpServletResponse used in MockMvc.
Closes gh-27230
Prior to this commit, MockMvc applied global ResultMatchers before
global ResultHandlers. This lead to unexpected scenarios where a
failing matcher would prevent a handler from being applied.
One concrete use case is `alwaysDo(print(System.err))` which should
print out MockMvc results for debugging purposes. However, if MockMvc is
configured with something like `alwaysExpect(content().string("?"))`
and the expectation fails, the user will never see the expected debug
output to help diagnose the problem.
This commit addresses this issue by applying global ResultHandlers
before ResultMatchers in MockMvc.
Closes gh-27225
Prior to this commit, it was possible to set the character encoding
in MockHttpServletResponse via setCharacterEncoding() or
setContentType(); however, those methods append "charset=..." to the
Content-Type header which may not be an acceptable side effect.
This commit addresses this shortcoming by introducing a new
setDefaultCharacterEncoding() in MockHttpServletResponse which allows
one to override the previously hard coded value of "ISO-8859-1". In
addition, setDefaultCharacterEncoding() does not modify the Content-Type
header.
The reset() method has also been updated to reset the character encoding
to the configured default character encoding.
Closes gh-27214
To slightly improve performance, this commit switches to
StringBuilder.append(char) instead of StringBuilder.append(String)
whenever we append a single character to a StringBuilder.
Closes gh-27098
This commit revises the fix submitted in 959e6d1745 by ensuring that
empty file input is converted to a MockPart with the supplied name, an
empty filename, "application/octet-stream" as the content type, and
empty content.
This aligns with the behavior of Servlet containers, as tested with the
interaction between Firefox and a standard Servlet running in a Jetty
Servlet container.
Closes gh-26799
Prior to this commit, when using HtmlUnit with empty file input,
MockMvc's HtmlUnitRequestBuilder would throw a NullPointerException
when attempting to create a MockPart based on the null File.
This commit ensures that empty file input is converted to a MockPart
with a valid name but with a null filename, a null content type, and
empty content.
Closes gh-26799
Prior to this commit, if a test class annotated with @DirtiesContext
and @EnabledIf/@DisabledIf with `loadContext = true` was disabled due
to the evaluated SpEL expression, the ApplicationContext would not be
marked as dirty and closed.
The reason is that @EnabledIf/@DisabledIf are implemented via JUnit
Jupiter's ExecutionCondition extension API which results in the entire
test class (as well as any associated extension callbacks) being
skipped if the condition evaluates to `disabled`. This effectively
prevents any of Spring's TestExecutionListener APIs from being invoked.
Consequently, the DirtiesContextTestExecutionListener does not get a
chance to honor the class-level @DirtiesContext declaration.
This commit fixes this by implementing part of the logic of
DirtiesContextTestExecutionListener in
AbstractExpressionEvaluatingCondition (i.e., the base class for
@EnabledIf/@DisabledIf support). Specifically, if the test class for an
eagerly loaded ApplicationContext is disabled,
AbstractExpressionEvaluatingCondition will now mark the test
ApplicationContext as dirty if the test class is annotated with
@DirtiesContext.
Closes gh-26694
Prior to this commit, MockHttpServletResponse only included the Expires
attribute in the generated Cookie header if the Max-Age attribute had
also been set.
This commit supports including the Expires attribute in the generated
Cookie Header even when the Max-Age attribute has not been set.
Closes gh-26558
This commit changes the condition in the if-block to check the number of
available processors instead of currently active threads with the hope
that doing so will prove more reliable on the CI server.
Previously this method returned headers only when a Content-Type part header
was present. Now it is guaranteed to return headers (possibly empty) as long
as there is a MultipartFile or Part with the given name.
Closes gh-26501
Prior to this commit, calls to setLocale() MockHttpServletResponse
would result in a NullPointerException if the supplied Locale was null.
Although the Javadoc for setLocale(Locale) and addHeader(String, String)
in javax.servlet.ServletResponse does not specify how a null
Locale should be handled, both Tomcat and Jetty simply ignore a null
value.
This commit therefore updates MockHttpServletResponse to silently
ignore a null Locale passed to setLocale().
Closes gh-26493
Prior to this commit, calls to setHeader() and addHeader() in
MockHttpServletResponse would result in an IllegalArgumentException or
NullPointerException if the supplied header value was null.
Although the Javadoc for setHeader(String, String) and
addHeader(String, String) in javax.servlet.http.HttpServletResponse
does not specify how a null header value should be handled, both Tomcat
and Jetty simply ignore a null value. Furthermore,
org.springframework.http.HttpHeaders.add(String, String) declares the
headerValue parameter as @Nullable.
This commit therefore updates MockHttpServletResponse to silently
ignore null header values passed to setHeader() and addHeader().
Closes gh-26488
Prior to this commit, if the DataSource in the
DataSourceFromTransactionManager was wrapped in a proxy implementing
InfrastructureProxy, SqlScriptsTestExecutionListener would throw an
exception stating that the DataSource in the ApplicationContext and the
DataSource in the DataSourceFromTransactionManager were not the same.
This commit unwraps both data sources and compares the underlying
target instances to check for equality.
In addition, this commit makes the unwrapResourceIfNecessary() method in
TransactionSynchronizationUtils public.
Closes gh-26422
This commit introduces a new feature in the Spring TestContext
Framework (TCF) that provides support for recording application events
published in the ApplicationContext so that assertions can be performed
against those events within tests. All events published during the
execution of a single test are made available via the ApplicationEvents
API which allows one to process the events as a java.util.Stream.
The following example demonstrates usage of this new feature.
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {
@Autowired
OrderService orderService;
@Autowired
ApplicationEvents events;
@Test
void submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Verify that an OrderSubmitted event was published
int numEvents = events.stream(OrderSubmitted.class).count();
assertThat(numEvents).isEqualTo(1);
}
}
To enable the feature, a test class must be annotated or meta-annotated
with @RecordApplicationEvents. Behind the scenes, a new
ApplicationEventsTestExecutionListener manages the registration of
ApplicationEvents for the current thread at various points within the
test execution lifecycle and makes the current instance of
ApplicationEvents available to tests via an @Autowired field in the
test class. The latter is made possible by a custom ObjectFactory that
is registered as a "resolvable dependency". Thanks to the magic of
ObjectFactoryDelegatingInvocationHandler in the spring-beans module,
the ApplicationEvents instance injected into test classes is
effectively a scoped-proxy that always accesses the ApplicationEvents
managed for the current test.
The current ApplicationEvents instance is stored in a ThreadLocal
variable which is made available in the ApplicationEventsHolder.
Although this class is public, it is only intended for use within the
TCF or in the implementation of third-party extensions.
ApplicationEventsApplicationListener is responsible for listening to
all application events and recording them in the current
ApplicationEvents instance. A single
ApplicationEventsApplicationListener is registered with the test's
ApplicationContext by the ApplicationEventsTestExecutionListener.
The SpringExtension has also been updated to support parameters of type
ApplicationEvents via the JUnit Jupiter ParameterResolver extension
API. This allows JUnit Jupiter based tests to receive access to the
current ApplicationEvents via test and lifecycle method parameters as
an alternative to @Autowired fields in the test class.
Closes gh-25616
This commit introduces computeAttribute() as an interface default method
in the AttributeAccessor API. This serves as a convenience analogous to
the computeIfAbsent() method in java.util.Map.
Closes gh-26281
gh-19930 introduced support for finding class-level test configuration
annotations on enclosing classes when using JUnit Jupiter @Nested test
classes, but support for @DynamicPropertySource methods got overlooked
since they are method-level annotations.
This commit addresses this shortcoming by introducing full support for
@NestedTestConfiguration semantics for @DynamicPropertySource methods
on enclosing classes.
Closes gh-26091
Prior to this commit, the test("test") conditions used in
AutowiredConfigurationErrorsIntegrationTests inadvertently asserted
that the invoked test methods reside in an org.springframework.test
subpackage, which is always the case for any test method in the
`spring-test` module. In other words, "test" is always a substring of
"org.springframework.test...", which is not a meaningful assertion.
This commit ensures that the JUnit Platform Test Kit is asserting the
actual names of test methods.
This commit introduces `and()` default methods in the MethodFilter and
FieldFilter functional interfaces in ReflectionUtils in order to simplify
uses cases that need to compose filter logic.
Closes gh-26063
Prior to this commit, a developer may have accidentally annotated a
JUnit Jupiter test method or lifecycle method with @Autowired, and that
would have potentially resulted in an exception that was hard to
understand. This is because the Spring container considers any
@Autowired method to be a "configuration method" when autowiring the
test class instance. Consequently, such an @Autowired method would be
invoked twice: once by Spring while attempting to autowire the test
instance and another time by JUnit Jupiter when invoking the test or
lifecycle method. The autowiring invocation of the method often leads
to an exception, either because Spring cannot satisfy a dependency (such
as JUnit Jupiter's TestInfo) or because the body of the method fails due
to test setup that has not yet been invoked.
This commit introduces validation for @Autowired test and lifecycle
methods in the SpringExtension that will throw an IllegalStateException
if any @Autowired method in a test class is also annotated with any of
the following JUnit Jupiter annotations.
- @Test
- @TestFactory
- @TestTemplate
- @RepeatedTest
- @ParameterizedTest
- @BeforeAll
- @AfterAll
- @BeforeEach
- @AfterEach
Closes gh-25966
Prior to this commit, the findAllLocalMergedAnnotations() method in
AnnotationDescriptor altered between the use of TYPE_HIERARCHY and
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES for the SearchStrategy, depending
on @NestedTestConfiguration semantics; however, when searching for
"local" annotations, there is no need to search the enclosing class
hierarchy since AnnotationDescriptor#next() handles that use case.
This commit therefore switches to using only the TYPE_HIERARCHY
strategy.
This commit also discontinues the use of
MergedAnnotationCollectors.toAnnotationSet() in order to avoid the
unnecessary creation of a temporary List when collecting synthesized
annotations in a LinkedHashSet.
Closes gh-25985
With this commit, bean definition profiles declared via @ActiveProfiles
are once again stored in registration order, in order to support use
cases in Spring Boot and other frameworks that depend on the
registration order.
This effectively reverts the changes made in conjunction with gh-25973.
Closes gh-26004
Previous incarnation of MockMvc Kotlin DSL tried to reuse directly
Java APIs like ModelResultMatchers or StatusResultMatchers, but
when using multiple matchers in DSL blocks like model { } or
status { }, only the last statement was taken in account which
was very confusing.
This refactoring provides dedicated Kotlin DSLs for matchers.
The main API breaking changes is that functions like isOk() need to be
invoked with the parenthesis, isOk is not supported anymore (on purpose).
Closes gh-24103
Prior to this commit, if a developer accidentally copied and pasted the
same @ContextConfiguration or @TestPropertySource declaration from a
test class to one of its subclasses or nested test classes, the Spring
TestContext Framework (TCF) would merge the inherited configuration
with the local configuration, resulting in different sets of
configuration metadata which in turn resulted in a different
ApplicationContext instance being loaded for the test classes. This
behavior led to unnecessary creation of identical application contexts
in the context cache for the TCF stored under different keys.
This commit ignores duplicate configuration metadata when generating
the ApplicationContext cache key (i.e., MergedContextConfiguration) in
the TCF. This is performed for the following annotations.
- @ContextConfiguration
- @ActiveProfiles (support already existed prior to this commit)
- @TestPropertySource
Specifically, if @ContextConfiguration or @TestPropertySource is
declared on a test class and its subclass or nested test class with the
exact same attributes, only one instance of the annotation will be used
to generate the cache key for the resulting ApplicationContext. The
exception to this rule is an "empty" annotation declaration. An empty
@ContextConfiguration or @TestPropertySource declaration signals that
Spring (or a third-party SmartContextLoader) should detect default
configuration specific to the annotated class. Thus, multiple empty
@ContextConfiguration or @TestPropertySource declarations within a test
class hierarchy are not considered to be duplicate configuration and
are therefore not ignored.
Since @TestPropertySource is a @Repeatable annotation, the same
duplicate configuration detection logic is applied for multiple
@TestPropertySource declarations on a single test class or test
interface.
In addition, this commit reinstates validation of the rules for
repeated @TestPropertySource annotations that was removed when support
for @NestedTestConfiguration was introduced.
Closes gh-25800
The migration from JUnit 4 assertions to AssertJ assertions resulted in
several unnecessary casts from int to long that actually cause
assertions to pass when they should otherwise fail.
This commit fixes all such bugs for the pattern `.isNotEqualTo((long)`.
Prior to this commit, two @ActiveProfiles declarations with the same
profiles but different order resulted in an identical, duplicate
ApplicationContext in the context cache in the Spring TestContext
Framework.
This commit uses a TreeSet to ensure that registered active profiles
are both unique and sorted, thereby avoiding cache misses for
semantically identical active profiles configuration on different test
classes.
Closes gh-25973
This commit introduces TestContextAnnotationUtils as a replacement for
MetaAnnotationUtils, with dedicated support for honoring the new
@NestedTestConfiguration annotation and related annotation search
semantics.
MetaAnnotationUtils has been reverted to its previous scope and is now
deprecated.
See gh-19930
This commit introduces support for discovering @Sql, @SqlGroup,
@SqlConfig, and @SqlMergeMode on enclosing classes for @Nested test
classes in JUnit Jupiter.
Closes gh-25913
Prior to this commit only Propagation.NOT_SUPPORTED was supported for
disabling test-managed transactions via the `propagation` attribute of
`@Transactional`.
This commit allows users to specify Propagation.NOT_SUPPORTED or
Propagation.NEVER to disable test-managed transactions.
Closes gh-25909
Prior to this commit, the EnclosingConfiguration mode used in
conjunction with @NestedTestConfiguration defaulted to INHERIT.
In other to allow development teams to change the default to OVERRIDE
(e.g., for compatibility with Spring Framework 5.0 through 5.2), this
commit introduces support for changing the default EnclosingConfiguration
mode globally via a JVM system property or via the SpringProperties
mechanism.
For example, the default may be changed to
EnclosingConfiguration.OVERRIDE by supplying the following JVM system
property via the command line.
-Dspring.test.enclosing.configuration=override
Closes gh-19930
Prior to this commit (and since Spring Framework 5.0), Spring's
integration with JUnit Jupiter supported detection of test
configuration (e.g., @ContextConfiguration, etc.) on @Nested classes.
However, if a @Nested class did not declare its own test configuration,
Spring would not find the configuration from the enclosing class. This
is in contrast to Spring's support for automatic inheritance of test
configuration from superclasses. The only workaround was to
copy-n-paste the entire annotation configuration from enclosing classes
to nested tests classes, which is cumbersome and error prone.
This commit introduces a new @NestedTestConfiguration annotation that
allows one to choose the EnclosingConfiguration mode that Spring should
use when searching for test configuration on a @Nested test class.
Currently, the options are INHERIT or OVERRIDE, where the current
default is OVERRIDE. Note, however, that the default mode will be
changed to INHERIT in a subsequent commit. In addition, support will be
added to configure the global default mode via the SpringProperties
mechanism in order to allow development teams to revert to the behavior
prior to Spring Framework 5.3.
As of this commit, inheritance of the following annotations is honored
when the EnclosingConfiguration mode is INHERIT.
- @ContextConfiguration / @ContextHierarchy
- @ActiveProfiles
- @TestPropertySource / @TestPropertySources
- @WebAppConfiguration
- @TestConstructor
- @BootstrapWith
- @TestExecutionListeners
- @DirtiesContext
- @Transactional
- @Rollback / @Commit
This commit does NOT include support for inheriting the following
annotations on enclosing classes.
- @Sql / @SqlConfig / @SqlGroup
In order to implement this feature, the search algorithms in
MetaAnnotationUtils (and various other spring-test internals) have been
enhanced to detect when annotations should be looked up on enclosing
classes. Other parts of the ecosystem may find the new
searchEnclosingClass() method in MetaAnnotationUtils useful to provide
similar support.
As a side effect of the changes in this commit, validation of user
configuration in repeated @TestPropertySource declarations has been
removed, but this may be reintroduced at a later date.
Closes gh-19930
The exchange() method is now deprecated because it is not safe for
general use but that doesn't apply to the WebTestClient because it
exposes a different higher level API for response handling that
ensures the response is consumed. Nevertheless WebTestClient cannot
call WebClient.exchange() any more.
To fix this WebTestClient no longer delegates to WebClient and thus
gains direct access to the underlying ExchangeFunction. This is
not a big deal because WebClient and WebTestClient do the same
only when it comes to gathering builder options and request input.
See gh-25751
Prior to this commit, some tests would belong to the PERFORMANCE
`TestGroup`, while they were not testing for performance but rather
performing functional tests that involve long running operations or
timeouts.
This commit moves those tests to the LONG_RUNNING `TestGroup`.
See gh-24830
UrlPathHelper is often created and used without customizations or with
the same customizations. This commit introduces re-usable, instances.
Effectively a backport of commit 23233c.
See gh-25690
This commit makes several changes in both WebMvc.fn as well as
WebFlux.fn.
- ServerRequest now exposes a RequestPath through requestPath(), and
pathContainer() has been deprecated.
- The PathPredicate and PathResourceLookupFunction now respects this
RequestPath's pathInApplication() in their path-related
functionality.
- When nesting, the PathPredicate now appends the matched part of the
path to the current context path, instead of removing the matched
part (which was done previously). This has the same result: the
matched part is gone, but now the full path stays the same.
Closes gh-25270