I accidentally introduced a regression in commit d185bb1d97 by no
longer checking the number of unique method names found when searching
for @TestBean factory methods.
This commit reintroduces that check and introduces a proper unit test
in TestBeanOverrideProcessorTests.
It turns out that we already had an integration test that was intended
to check for this scenario; however, that test actually did not test
this scenario due to a copy-and-paste error. Thus, this commit also
updates TestBeanInheritanceIntegrationTests so that
fieldInSupertypeWithPrioritizedFactoryMethodInSubtype() tests what it's
supposed to.
This commit benefits from a feature introduced in AssertJ 3.26.0, see
https://github.com/assertj/assertj/pull/3377.
This allows to use any AssertFactory and convert the actual value to
the type the factory manages. Previously, we were using
ParameterizedTypeReference to express the type with its generic
signature but the returned assert object would not be narrowed to the
converted type.
Thanks to this change, we can request to convert the actual value to
`InstanceOfAssertFactories.list(Member.class)` and get a `ListAssert`
of `Member` as a result, rather than an `ObjectAssert` of `List<User>`.
Thanks very much to @scordio for his efforts.
Closes gh-32953
Prior to this commit, a @TestBean factory method was found in the
directly enclosing class for a @Nested test class; however, such a
factory method was not found in the enclosing class of the enclosing
class, etc.
This commit updates the search algorithm for @TestBean factory methods
so that it recursively searches the enclosing class hierarchy for
@Nested test classes.
Closes gh-32951
- Do not silently abort bean override processing if the ApplicationContext
is not a BeanDefinitionRegistry.
- Include conflicting bean names in error messages instead of just the
number of conflicting beans.
- Consistently throw IllegalStateException.
- etc.
Prior to this commit, @TestBean factory methods were required to be
defined in the test class or one of its superclasses. However, users
may wish to define common factory methods in interfaces that can be
shared easily across multiple test classes simply by implementing the
necessary interface(s).
This commit therefore switches from ReflectionUtils to
MethodIntrospector to find @TestBean factory methods in implemented
interfaces within the type hierarchy of the test class.
Closes gh-32943
As a follow up to the previous commit (31f8e12adb), this commit
polishes bean override tests and revises them with a focus on AOT
testing support and simplified maintenance.
- Introduce EngineTestKitUtils to simplify working with JUnit's
EngineTestKit.
- Use idiomatic EngineTestKit APIs to simplify assertions on
EngineTestKit results.
- Introduce BeanOverrideTestSuite to simplify running all bean override
tests within the IDE.
- Separate failure and success scenario tests, so that failure tests do
not launch the JUnit Platform to run tests using the Spring
TestContext Framework (TCF) within a test class that itself uses the
TCF.
- Make AbstractTestBeanIntegrationTestCase actually abstract.
- Rename test case classes to give them meaningful names and simplify
understanding of what's being tested.
- Ensure tests for @MockitoSpyBean functionality use @MockitoSpyBean
instead of @MockitoBean.
- Declare @Configuration classes local to @SpringJUnitConfig test
classes whenever possible.
See gh-29122
See gh-32925
This commit moves the features of MockHttpServletRequestBuilder in
an abstract class so that another class can offer the same feature
whilst providing AssertJ support. This wasn't possible previously as
the builder's return type would lose the concrete builder types.
This change benefits MockMultipartHttpServletRequestBuilder that can
use the same mechanism to offer additional settings.
This change also makes it so that a builder instance can be created
using only the HttpMethod. Previously, the URI had to be provided as
well and that makes it impossible to specify it using the builder.
See gh-32913
Previously, AbstractJsonContentAssert worked on a raw String, which
made standard AssertJ nested calls, such as satisfies, to return an
assert on the raw string, rather than one with JSON support.
This commit rework AbstractJsonContentAssert so that it no longer
extend from AbstractStringAssert. This makes the list of methods more
focused on JSON assertions, and allow standard operations to provide
the right assert object.
Closes gh-32894
This commit makes several improvements to MultiValueMap:
- asSingleValueMap offers a single-value view (as opposed to the
existing toSingleValueMap, which offers a copy)
- fromSingleValue is a static method that adapts a Map<?,?> to the
MultiValueMap interface
- fromMultiValue is a static method that adapts a Map<?,List<?>> to the
MultiValueMap interface
Closes gh-32832
This commit introduces a JsonComparator abstraction, with an
implementation using JSONAssert. Previously, JSONAssert was the only
choice.
Test APIs have been adapted to allow the new abstraction while relying
on JSONAssert still for high-level methods.
Closes gh-32791
Co-authored-by: Stéphane Nicoll <stephane.nicoll@broadcom.com>
This commit changes the FormHttpMessageConverter from a
HttpMessageConverter<MultiValueMap<String, ?>> to a
HttpMessageConverter<Map<String, ?>>, so that normal, single-value maps
can also be used as form representation, both for reading and writing.
Closes gh-32826
Prior to this commit, as a result of commit 6cdb34410b, the
DynamicValuesPropertySource was eagerly registered in the Environment
even if the DynamicPropertyRegistry was never used to register dynamic
properties.
This commit ensures that the DynamicValuesPropertySource is only
registered if we know that the DynamicPropertyRegistry is actually used
-- either by a @DynamicPropertySource method in a test class or via a
bean in the ApplicationContext that invokes add() on the
DynamicPropertyRegistry bean.
See gh-32271
Closes gh-32871
This commit uses the bean factory `isAutowiredCandidate` method directly
in `BeanOverrideBeanFactoryPostProcessor` to select a single match among
multiple candidates when matching by type.
The expected consequence, in most cases, is that this will delegate to
a `@Qualifier`-aware `QualifierAnnotationAutowireCandidateResolver`.
In that sense, bean overriding by-type matching is now potentially
taking Qualifier annotations or meta-annotations into account.
It also changes the way existing bean definitions are checked in case
a bean name has been specified: factory beans are now taken into account
when checking the type of an existing definition matches the expected
bean override type.
Closes gh-32822
This commit avoids several instances of MockHttpServletRequest to
have a common reader for empty content as closing it will have an
unwanted side effect on the others.
Closes gh-32820
Prior to this commit, DynamicPropertyRegistry could only be used with a
static @DynamicPropertySource method in an integration test class;
however, it can also be useful to be able to register a "dynamic
property" from within a test's ApplicationContext -- for example, in a
@Bean method in a @Configuration class that is specific to testing
scenarios.
To support such use cases, this commit updates the dynamic property
source infrastructure so that a DynamicPropertyRegistry is always
registered as a singleton bean in a test's ApplicationContext. This
allows DynamicPropertyRegistry to be autowired into a @Configuration
class or supplied to a @Bean method as an argument as shown in the
following example.
@Bean
@DynamicPropertySource
ApiServer apiServer(DynamicPropertyRegistry registry) {
ApiServer apiServer = new ApiServer();
registry.add("api.url", apiServer::getUrl);
return apiServer;
}
Note that the use of @DynamicPropertySource on the @Bean method is
optional and results in the corresponding bean being eagerly
initialized so that other singleton beans in the context can be given
access to the dynamic properties sourced from that bean when those
other beans are initialized.
Side note: DynamicPropertySourceBeanInitializer temporarily implements
LoadTimeWeaverAware since doing so is currently the only way to have a
component eagerly initialized before the
ConfigurableListableBeanFactory.preInstantiateSingletons() phase.
However, we plan to introduce a first-class callback to support such
use cases in the future.
Closes gh-32271
This commit introduces a `isNotEqualTo(String)` assertion in order to
avoid false negatives when using the default Object-based assertion.
This is a risk since MediaTypeAssert has a `isEqualTo(String)` method
that overrides the base object method and parses the provided String
into a MediaType. Users may thus be tempted to use the reverse assertion
and expect the same parsing behaviour.
This commit also adds tests around the String parsing and the isNotEqual
cases.
Closes gh-32756
This change switches default behavior of `@TestBean`, `@MockitoBean` and
`@MockitoSpyBean` to match the bean definition / bean to override by
type in the case there is no explicit bean name provided via the
annotation. The previous behavior of using the annotated field's name
is still an option for implementors, but no longer the default.
Closes gh-32761
Prior to this commit, trailing slash matching was disabled by default in
Spring MVC with gh-28552. `StandaloneMockMvcBuilder` was not changed as
a result and still had the trailing slash match option enabled.
This commit aligns the defaults in `StandaloneMockMvcBuilder` to better
reflect the expected behavior in tests.
Closes gh-32796
This commit renames AssertableMockMvc to MockMvcTester as the former
was too mouthful, and we are looking for a naming pattern that can be
applied consistently to test utilities that rely on AssertJ.
Rather than AssertableMvcResult, we now use MvcTestResult and it no
longer extends from MvcResult itself. That avoids having existing code
that is migrated to the new API whilst assigning the result to MvcResult
and get a bad DevXP as that doesn't bring any of the AssertJ support.
See gh-32712
This commit removes ResponseBodyAssert and rather offers first-class
access support for the response body at the root level using bodyText(),
bodyJson(), and body().
This avoids a double navigation to assert the response body.
See gh-32712
This commit merges the JSONCompare and JsonPath support in a single
assert object. The rationale for the previous situation was that
JsonPath is optional but merging the assertions methods do not make
it mandatory as the usage if JsonPath is triggered only if it is
actually used.
See gh-32712
Previously, if a factory method is defined on a parent, the generated
code would blindly use the method's declaring class for both the target
of the generated code, and the signature of the method.
This commit improves the resolution by considering the factory metadata
in the BeanDefinition.
Closes gh-32609
This commit improves on the bean overriding feature in several ways:
the API is simplified and polished (metadata and processor contracts,
etc...).
The commit also reworks infrastructure classes (context customizer,
test execution listener, BeanOverrideBeanFactoryPostProcessor, etc...).
Parsing of annotations is now fully stateless.
In order to avoid OverrideMetadata in bean definition and to make a
first step towards AOT support, the BeanOverrideBeanFactoryPostProcessor
now delegates to a BeanOverrideRegistrar to track classes to parse,
the metadata-related state as well as for the field injection methods
for tests.
Lastly, this commit increases the test coverage for the provided
annotations and adds integration tests and fixes a few `@TestBean`
issues.
This commit makes the use of bean definition overriding more visible and
prepare for a deprecation of the feature in the next major release.
As of this commit, use of bean definition overriding logs at INFO level.
The previous log level can be restored by setting the
allowBeanDefinitionOverriding flag explicitly on the BeanFactory (or
via the related ApplicationContext).
A number of tests that are using bean overriding on purpose have been
updated to set this flag, which will make them easier to find once we
actually deprecate the feature.
Closes gh-31288
The ApplicationContext has a resource cache that it uses while the
context is being refreshed. Once the refresh phase completes, the
cache is cleared as its content is no longer in use. If beans are
lazily initialized, there is a case where the cache might be filled
again as processing is deferred past the refresh phase.
For those cases, the cache is not cleared. This can be a problem when
running in this mode with a large set of test configurations as the TCF
caches the related contexts.
This commit includes an additional TestExecutionListener to the TCF that
clears the resource cache if necessary once a test class completes. It
is ordered so that it runs after `@DirtiesContext` support as marking
the context as dirty will remove it from the cache and make that extra
step unnecessary.
Closes gh-30954
This commit adds a new way to use MockMvc by returning a MvcResult that
is AssertJ compatible. Compared to the existing MockMvc infrastructure,
this new model has the following advantages:
* Can be created from a MockMvc instance
* Dedicated builder methods for easier setup
* Assertions use familiar AssertJ syntax with discoverable assertions
through a DSL
* Improved exception message
See gh-21178
Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
This commit adds AssertJ compatible assertions for HTTP request and
responses, including headers, media type, and response body. The latter
delegate to the recently included JSON assert support.
See gh-21178
Historically, we have rarely intentionally thrown a
NullPointerException in the Spring Framework. Instead, we prefer to
throw either an IllegalArgumentException or IllegalStateException
instead of a NullPointerException.
However, changes to the code in recent times have introduced the use of
Objects.requireNonNull(Object) which throws a NullPointerException
without an explicit error message.
The latter ends up providing less context than a NullPointerException
thrown by the JVM (since Java 14) due to actually de-referencing a
null-pointer. See https://openjdk.org/jeps/358.
In light of that, this commit revises our current use of
Objects.requireNonNull(Object) by removing it or replacing it with
Assert.notNull().
However, we still use Objects.requireNonNull(T, String) in a few places
where we are required to throw a NullPointerException in order to
comply with a third-party contract such as Reactive Streams.
Closes gh-32430