Commit Graph

1519 Commits

Author SHA1 Message Date
Sam Brannen
374c3b4545 Provide complete support for qualifier annotations with Bean Overrides
Prior to this commit, the Test Bean Override feature provided support
for overriding beans based on qualifier annotations in several
scenarios; however, qualifier annotations got lost if they were
declared on the return type of the @⁠Bean method for the bean being
overridden and the @⁠BeanOverride (such as @⁠MockitoBean) was based on
a supertype of that return type.

To address that, this commit sets the @⁠BeanOverride field as the
"qualified element" in the RootBeanDefinition to ensure that qualifier
annotations are available for subsequent autowiring candidate
resolution.

Closes gh-34646
2025-03-27 15:29:14 +01:00
Sam Brannen
d7e470d3e0 Polishing 2025-03-27 14:05:25 +01:00
Sam Brannen
208d52d852 Introduce Checkstyle rule for separator symbol location 2025-03-19 15:35:44 +01:00
Sam Brannen
c6a9aa59a3 Remove BDDMockito Checkstyle rule
This commit removes the BDDMockito Checkstyle rule, since it did not
actually enforce the use of BDDMockito.

This commit also updates static imports to use Mockito instead of
BDDMockito where appropriate (automated via the Eclipse IDE Organize
Imports clean-up task).

Closes gh-34616
2025-03-18 16:35:57 +01:00
Sam Brannen
020f556841 Support custom attribute with a value in MockCookie.parse()
Prior to this commit, MockCookie.parse() failed with an
IllegalArgumentException when attempting to parse a custom attribute
with a value, such as "Version=1". This is a regression that was
inadvertently introduced in 7fc4937199
when adding support for the "Partitioned" attribute which does not
support a value.

This commit addresses this regression by parsing both the name and the
value from an optional, custom attribute.

See gh-31454
Closes gh-34575
2025-03-12 11:19:38 +01:00
Sam Brannen
e421104cf3 Supply RuntimeHints to an AotContextLoader
This commit introduces a new loadContextForAotProcessing(...) variant
in AotContextLoader which accepts a RuntimeHints argument. This new
method is an interface default method which delegates to the existing
loadContextForAotProcessing(MergedContextConfiguration) variant for
backward compatibility.

In addition, the original loadContextForAotProcessing(...) variant is
now deprecated and has been converted to an interface default method
which throws an UnsupportedOperationException.

Note, however, that the framework now only invokes the new
loadContextForAotProcessing(...) variant within TestContextAotGenerator.

Closes gh-34513
2025-03-03 17:39:56 +01:00
Sam Brannen
adfeba23e0 Polishing 2025-03-03 17:39:47 +01:00
Brian Clozel
b6a5402d88 Fix multiple Content-Language values in MockHttpServletResponse
Prior to this commit, `MockHttpServletResponse` would only support
adding a `Content-Language` once. Adding multiple header values would
always replace the content-language property in the response and the
entire header value.

This commit ensures that this behavior is supported.

Fixes gh-34488
2025-02-25 17:11:37 +01:00
Brian Clozel
6dd73ae3e4 Handle null values in MockHttpServletResponse#setHeader
Prior to this commit, `MockHttpServletResponse#setHeader` would not
remove the header entry when given a `null` value, as documented in the
Servlet API.
This commit ensures that this behavior is enforced.

Fixes gh-34464
2025-02-21 14:23:12 +01:00
Sam Brannen
e31ce359a1 Support @⁠MockitoSpyBean at the type level on test classes
Prior to this commit, @⁠MockitoSpyBean could only be declared on fields
within test classes, which prevented developers from being able to
easily reuse spy configuration across a test suite.

With this commit, @⁠MockitoSpyBean is now supported at the type level
on test classes, their superclasses, and interfaces implemented by
those classes. @⁠MockitoSpyBean is also supported on enclosing classes
for @⁠Nested test classes, their superclasses, and interfaces
implemented by those classes, while honoring @⁠NestedTestConfiguration
semantics.

In addition, @⁠MockitoSpyBean:

- has a new `types` attribute that can be used to declare the type or
  types to spy when @⁠MockitoSpyBean is declared at the type level

- can be declared as a repeatable annotation at the type level

- can be declared as a meta-annotation on a custom composed annotation
  which can be reused across a test suite (see the @⁠SharedSpies
  example in the reference manual)

To support these new features, this commit also includes the following
changes.

- MockitoSpyBeanOverrideProcessor has been revised to support
  @⁠MockitoSpyBean at the type level.

- The "Bean Overriding in Tests" and "@⁠MockitoBean and
  @⁠MockitoSpyBean" sections of the reference manual have been fully
  revised.

See gh-34408
Closes gh-33925
2025-02-12 15:54:54 +01:00
Sam Brannen
d59991fcc9 Revise contribution
See gh-34006
2025-02-10 11:42:24 +01:00
Yanming Zhou
cf46f391d7 Improve diagnostics when a Bean Override cannot be selected by type
See gh-34004
Closes gh-34006

Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
2025-02-10 11:06:10 +01:00
Sam Brannen
305686dbf7 Ensure Bean Overrides are discovered once in @⁠Nested hierarchies
Changes made to the Bean Override search algorithms in commit
9181cce65f resulted in a regression that caused tests to start failing
due to duplicate BeanOverrideHandlers under the following circumstances.

- An enclosing class (typically a top-level test class) declares a
  @⁠BeanOverride such as @⁠MockitoBean.

- An inner class is declared in that enclosing class.

- A @⁠Nested test class which extends that inner class is declared in
  the same enclosing class.

The reason for the duplicate detection is that the current search
algorithm visits the common enclosing class twice.

To address that, this commit revises the search algorithm in
BeanOverrideHandler so that enclosing classes are only visited once.

See gh-33925
Closes gh-34324
2025-02-08 13:30:22 +01:00
Sam Brannen
ba56c1a8f4 Fix copy-n-paste error 2025-02-07 18:26:26 +01:00
Sam Brannen
9107f7b592 Honor @⁠Primary before fallback qualifier for Bean Overrides
Prior to this commit, test bean overrides (for example, @⁠MockitoBean,
@⁠TestBean, etc.) eagerly honored the name of the annotated field as a
fallback qualifier, effectively ignoring @⁠Primary and @⁠Fallback
semantics for certain use cases.

This led to situations where a bean override for a test would select a
different bean than the core container would for the same autowiring
metadata.

To address that, this commit revises the implementation of
BeanOverrideBeanFactoryPostProcessor so that @⁠Primary and @⁠Fallback
semantics are consistently honored before attempting to use the
annotated field's name as a fallback qualifier.

Closes gh-34374
2025-02-07 18:06:52 +01:00
rstoyanchev
7f29f0e663 Revert commit 3505c4bcad
The fix did not address the issue. It only made the constructor not
fail with tests succeeding due to setter binding instead.

See gh-34043
2025-02-05 14:26:12 +00:00
rstoyanchev
3898482d3f Revert commit 0f38c28e91
The fix is not how the issue needs to be addressed.

See gh-34121
2025-02-05 14:26:12 +00:00
Sam Brannen
cecebd0ef1 Polish HttpHeadersAssertTests 2025-01-20 16:48:10 +01:00
Sam Brannen
9181cce65f Support @⁠MockitoBean at the type level on test classes
Prior to this commit, @⁠MockitoBean could only be declared on fields
within test classes, which prevented developers from being able to
easily reuse mock configuration across a test suite.

With this commit, @⁠MockitoBean is now supported at the type level on
test classes, their superclasses, and interfaces implemented by those
classes. @⁠MockitoBean is also supported on enclosing classes for
@⁠Nested test classes, their superclasses, and interfaces implemented
by those classes, while honoring @⁠NestedTestConfiguration semantics.

In addition, @⁠MockitoBean:

- has a new `types` attribute that can be used to declare the type or
  types to mock when @⁠MockitoBean is declared at the type level

- can be declared as a repeatable annotation at the type level

- can be declared as a meta-annotation on a custom composed annotation
  which can be reused across a test suite (see the @⁠SharedMocks
  example in the reference manual)

To support these new features, this commit also includes the following
changes.

- The `field` property in BeanOverrideHandler is now @⁠Nullable.

- BeanOverrideProcessor has a new `default` createHandlers() method
  which is invoked when a @⁠BeanOverride annotation is found at the
  type level.

- MockitoBeanOverrideProcessor implements the new createHandlers()
  method.

- The internal findHandlers() method in BeanOverrideHandler has been
  completely overhauled.

- The @⁠MockitoBean and @⁠MockitoSpyBean section of the reference
  manual has been completely overhauled.

Closes gh-33925
2025-01-15 17:13:35 +01:00
Stéphane Nicoll
d280358e98 Fix typo in HttpHeadersAssert#doesNotContainHeaders
This commit deprecates the existing doesNotContainsHeaders in favor of a
newly introduced method that does not have the typo.

Closes gh-34263
2025-01-15 11:19:00 +01:00
Sam Brannen
ff9b7678a7 Polish Bean Override internals and tests 2025-01-14 11:25:37 +01:00
Sam Brannen
cd2fbb1ec5 Properly resolve @⁠TestBean factory method within class hierarchy
Prior to this commit, the search algorithm used to locate a @⁠TestBean
factory method within a test class hierarchy incorrectly found factory
methods declared in subclasses or nested test classes "below" the class
in which the @⁠TestBean field was declared. This resulted in "duplicate
bean override" failures for @⁠TestBean overrides which are clearly not
duplicates but rather "overrides of an override".

This commit ensures that @⁠TestBean factory method resolution is
consistent in type hierarchies as well as in enclosing class
hierarchies (for @⁠Nested test classes) by beginning the search for a
factory method in the class which declares the @⁠TestBean field.

Closes gh-34204
2025-01-07 17:20:52 +02:00
Sam Brannen
e8745522fc Polishing 2025-01-07 11:52:33 +02:00
Sam Brannen
181db1db75 Update copyright headers to 2025 2025-01-05 17:00:47 +02:00
Sam Brannen
ef4f1f0a71 Ensure @⁠BeanOverride in subclass takes precedence over superclass
Prior to this commit, a @⁠BeanOverride (such as @⁠TestBean) for a
specific target bean which was declared in a superclass always took
precedence over a bean override for the same target bean in a subclass,
thereby rendering the bean override configuration in the subclass
useless. In other words, there was no way for a test class to override
a bean override declared in a superclass.

To address that, this commit switches from direct use of
ReflectionUtils.doWithFields() to a custom search algorithm that
traverses the class hierarchy using tail recursion for processing
@⁠BeanOverride fields (delegating now to
ReflectionUtils.doWithLocalFields() in order to continue to benefit
from the caching of declared fields in ReflectionUtils).

Closes gh-34194
2025-01-04 18:27:05 +02:00
Sam Brannen
019f76468b Revise TestBeanForInheritanceIntegrationTests
Amongst other refinements, this commit ensures that the "anotherBean"
use case is actually tested.
2025-01-04 14:16:59 +02:00
Sam Brannen
2ba0022704 Polishing 2025-01-03 17:26:45 +02:00
Simon Baslé
0f38c28e91 Fix ServletRequestDataBinder ctor binding with []-indexed query params
This change ensures that a request containing query parameters in the
array format `someArray[]=value` can be bound into a simple array in
constructors, even for cases where the array values don't have nested
properties.

The value resolver is directly called in the constructor case, before
any mutable properties are considered or even cleared (see
`WebDataBinder#adaptEmptyArrayIndices` method). As a result, we need to
accommodate the possibility that the request stores array elements under
the `name[]` key rather than `name`. This change attempts a secondary
lookup with the `[]` suffix if the type is a list or array, and the key
doesn't include an index.

Closes gh-34121
2024-12-27 10:59:15 +01:00
Simon Baslé
3505c4bcad Ensure DataBinder can bind constructor with a Map with simple values
This change ensures that DataBinder can bind constructor with a Map
parameter that has no nested properties, but just simple values like
a String: `someMap[0]=exampleString`.

Integration tests have been added to cover similar cases that use the
ServletRequestDataBinder.

Closes gh-34043
2024-12-27 10:57:30 +01:00
Brian Clozel
52006b71bc Support Servlet error message in MockMvc assertions
Prior to this commit, `MockMvc` would support checking for the Servlet
error message as the "response status reason". While this error message
can be driven with the `@ResponseStatus` annotation, this message is not
technically the HTTP status reason listed on the response status line.

This message is provided by the Servlet container in the error page when
the `response.sendError(int, String)` method is used.

This commit adds the missing
`mvc.get().uri("/error/message")).hasErrorMessage("error message")`
assertion to check for this Servlet error message.

Closes gh-34016
2024-12-11 16:00:41 +01:00
Sam Brannen
54948a4e88 Log warning when one Bean Override overrides another Bean Override
It is currently possible for one Bean Override to override another
logically equivalent Bean Override.

For example, a @⁠TestBean can override a @⁠MockitoBean, and vice versa.

In fact, it's also possible for a @⁠MockitoBean to override another
@⁠MockitoBean, for a @⁠TestBean to override a @⁠TestBean, etc.

However, there may be viable use cases for one override overriding
another override. For example, one may have a need to spy on a bean
created by a @⁠TestBean factory method.

In light of that, we do not prohibit one Bean Override from overriding
another Bean Override; however, with this commit we now log a warning
to help developers diagnose issues in case such an override is
unintentional.

For example, given the following test class, where TestConfig registers
a single bean of type MyService named "myService"...

@⁠SpringJUnitConfig(TestConfig.class)
class MyTests {

  @⁠TestBean(methodName = "example.TestUtils#createMyService")
  MyService testService;

  @⁠MockitoBean
  MyService mockService;

  @⁠Test
  void test() {
    // ...
  }
}

... running that test class results in a log message similar to the
following, which has been formatted for readability.

WARN - Bean with name 'myService' was overridden by multiple handlers:
[
 [TestBeanOverrideHandler@44b21f9f
  field = example.MyService example.MyTests.testService,
  beanType = example.MyService,
  beanName = [null],
  strategy = REPLACE_OR_CREATE
 ],
 [MockitoBeanOverrideHandler@7ee8130e
  field = example.MyService example.MyTests.mockService,
  beanType = example.MyService,
  beanName = [null],
  strategy = REPLACE_OR_CREATE,
  reset = AFTER,
  extraInterfaces = set[[empty]],
  answers = RETURNS_DEFAULTS, serializable = false
 ]
]

NOTE: The last registered BeanOverrideHandler wins. In the above
example, that means that @⁠MockitoBean overrides @⁠TestBean, resulting
in a Mockito mock for the MyService bean in the test's
ApplicationContext.

Closes gh-34056
2024-12-10 12:49:48 +01:00
Sam Brannen
cd60a0013b Reject identical Bean Overrides
Prior to this commit, the Bean Override feature in the Spring
TestContext Framework (for annotations such as @⁠MockitoBean and
@⁠TestBean) silently allowed one bean override to override another
"identical" bean override; however, Spring Boot's @⁠MockBean and
@⁠SpyBean support preemptively rejects identical overrides and throws
an IllegalStateException to signal the configuration error to the user.

To align with the behavior of @⁠MockBean and @⁠SpyBean in Spring Boot,
and to help developers avoid scenarios that are potentially confusing
or difficult to debug, this commit rejects identical bean overrides in
the Spring TestContext Framework.

Note, however, that it is still possible for a bean override to
override a logically equivalent bean override. For example, a
@⁠TestBean can override a @⁠MockitoBean, and vice versa.

Closes gh-34054
2024-12-09 16:00:24 +01:00
Sam Brannen
2137750591 Improve Bean Override by-name integration tests 2024-12-09 13:01:42 +01:00
Sam Brannen
aee52b53a1 Introduce MockitoAssertions 2024-12-08 18:41:10 +01:00
Sam Brannen
92bbaa21e0 Improve test coverage for @⁠MockitoSpyBean use cases 2024-12-08 18:41:10 +01:00
Sam Brannen
aa7b459803 Fix Phantom Read problem for Bean Overrides in the TestContext framework
To make an analogy to read phenomena for transactional databases, this
commit effectively fixes the "Phantom Read" problem for Bean Overrides.

A phantom read occurs when the BeanOverrideBeanFactoryPostProcessor
retrieves a set of bean names by-type twice and a new bean definition
for a compatible type has been created in the BeanFactory by a
BeanOverrideHandler between the first and second retrieval.

Continue reading for the details...

Prior to this commit, the injection of test Bean Overrides (for
example, when using @⁠MockitoBean) could fail in certain scenarios if
overrides were created for nonexistent beans "by type" without an
explicit name or qualifier. Specifically, if an override for a SubType
was created first, and subsequently an attempt was made to create an
override for a SuperType (where SubType extends SuperType), the
override for the SuperType would "override the override" for the
SubType, effectively removing the override for the SubType.
Consequently, injection of the override instance into the SubType field
would fail with an error message similar to the following.

BeanNotOfRequiredTypeException: Bean named 'Subtype#0' is expected to
be of type 'Subtype' but was actually of type 'Supertype$Mock$XHb7Aspo'

This commit addresses this issue by tracking all generated bean names
(in a generatedBeanNames set) and ensuring that a new bean override
instance is created for the current BeanOverrideHandler if a previous
BeanOverrideHandler already created a bean override instance that now
matches the type required by the current BeanOverrideHandler.

In other words, if the generatedBeanNames set already contains the
beanName that we just found by-type, we cannot "override the override",
because we would lose one of the overrides. Instead, we must create a
new override for the current handler. In the example given above, we
must end up with overrides for both SuperType and SubType.

Closes gh-34025
2024-12-07 16:51:16 +01:00
Sam Brannen
51956fad89 Test MockReset strategy for @⁠MockitoSpyBean
As a follow up to commit 0088b9c7f8, this commit introduces an
integration test which verifies that a spy created via @⁠MockitoSpyBean
using the MockReset.AFTER strategy is not reset between the refresh of
the ApplicationContext and the first use of the spy within a @⁠Test
method.

See gh-33941
See gh-33986
2024-11-29 11:27:02 +01:00
Sam Brannen
0088b9c7f8 Honor MockReset strategy for @⁠MockitoBean and @⁠MockitoSpyBean
Commit 6c2cba5d8a introduced a regression by inadvertently removing the
MockReset strategy comparison when resetting @⁠MockitoBean and
@⁠MockitoSpyBean mocks.

This commit reinstates the MockReset strategy check and introduces
tests for this feature.

Closes gh-33941
2024-11-26 11:54:46 +01:00
Sam Brannen
b9cf03f8f0 Construct consistent error messages in BeanOverrideBeanFactoryPostProcessor 2024-11-21 11:28:31 +01:00
Sam Brannen
08a789cee9 Honor @⁠Fallback semantics for Test Bean Overrides
Closes gh-33924
2024-11-21 09:50:17 +01:00
Sam Brannen
3569cfe990 Reject static Bean Override fields for @⁠MockitoBean, @⁠TestBean, etc.
Closes gh-33922
2024-11-20 11:29:01 +01:00
Sam Brannen
a3c132c442 Polish XmlExpectationsHelper[Tests] 2024-11-19 13:33:20 +01:00
boiarshinov
91791c1756 Fail with full description for XML diff in XmlExpectationsHelper
Closes gh-33827
2024-11-19 13:23:58 +01:00
Sam Brannen
a3b979c5ec Register runtime hints for @⁠TestBean fully-qualified method names
This commit introduces a TestBeanReflectiveProcessor that registers
GraalVM native image reflection hints for a fully-qualified method name
configured via @⁠TestBean.

Closes gh-33836
2024-11-01 16:48:06 +01:00
Sam Brannen
a8f5848a5d Add missing runtime hint assertions for Bean Overrides 2024-11-01 16:47:36 +01:00
Sam Brannen
97f23dca22 Remove extra "Bean" from TestBeanBeanOverrideHandler class name
The extra "Bean" was accidentally added in commit ebdf6dc2fc.

See gh-33702
2024-11-01 14:57:30 +01:00
Sam Brannen
86784b61cc Introduce support for a custom reason in @⁠DisabledInAotMode
Closes gh-33833
2024-11-01 12:24:53 +01:00
Sam Brannen
ba692aa3ef Honor MockReset without @⁠MockitoBean or @⁠MockitoSpyBean fields
Prior to this commit, the static factory methods in MockReset (such as
MockReset.before() and MockReset.after()) could only be applied to
beans within the ApplicationContext if the test class declared at least
one field annotated with either @⁠MockitoBean or @⁠MockitoSpyBean.

However, the Javadoc states that it should be possible to apply
MockReset directly to any mock in the ApplicationContext using the
static methods in MockReset.

To address that, this commit reworks the "enabled" logic in
MockitoResetTestExecutionListener as follows.

- We no longer check for the presence of annotations from the
  org.springframework.test.context.bean.override.mockito package to
  determine if MockReset is enabled.

- Instead, we now rely on a new isEnabled() method to determine if
  MockReset is enabled.

The logic in the isEnabled() method still relies on the mockitoPresent
flag as an initial check; however, mockitoPresent only determines if
Mockito is present in the classpath. It does not determine if Mockito
can actually be used. For example, it does not detect if the necessary
reachability metadata has been registered to use Mockito within a
GraalVM native image.

To address that last point, the isEnabled() method performs an
additional check to determine if Mockito can be used in the current
environment. Specifically, it invokes Mockito.mockingDetails().isMock()
which in turn initializes core Mockito classes without actually
attempting to create a mock. If that fails, that means that Mockito
cannot actually be used in the current environment, which typically
indicates that GraalVM reachability metadata has not been registered
for the org.mockito.plugins.MockMaker in use (such as the
ProxyMockMaker).

In addition, isEnabled() lazily determines if Mockito can be
initialized, since attempting to detect that during static
initialization results in a GraalVM native image error stating that
Mockito internals were "unintentionally initialized at build time".

If Mockito cannot be initialized, MockitoResetTestExecutionListener
logs a DEBUG level message providing access to the corresponding stack
trace, and MockReset support is disabled.

Closes gh-33829
2024-10-31 15:56:54 +01:00
Sam Brannen
0846706688 Adapt copyright headers copied from Spring Boot 2024-10-31 15:33:50 +01:00
Sam Brannen
4a0edc59cc Verify support for MockReset for beans within the ApplicationContext
This commit verifies that MockReset.before() and MockReset.after() are
supported for beans within the ApplicationContext.

However, the test class must declare a field annotated with either
@⁠MockitoBean or @⁠MockitoSpyBean in order for the MockReset feature to
be triggered.

See gh-33742
2024-10-30 17:31:15 +01:00