Opened 15 months ago
Last modified 5 months ago
#59482 assigned task (blessed)
Tests: Introduce Reflection API helper methods.
Reported by: | costdev | Owned by: | costdev |
---|---|---|---|
Milestone: | Future Release | Priority: | normal |
Severity: | normal | Version: | |
Component: | Build/Test Tools | Keywords: | has-patch has-unit-tests |
Focuses: | Cc: |
Description (last modified by )
In unit tests, the Reflection API is often used to get or set a private
or protected
property's value, or to invoke a private
or protected
method.
To do so, the Reflection<Method|Property>::setAccessible()
method must be called with true
before use, then should be called again with false
afterwards. There are quite a lot of instances in the test suite where the Reflection API is used, and for a decent number of these, accessibility is not reset.
For brevity, much like assertSameSets()
, and to ensure that accessibility is always reset, this ticket aims to introduce new methods to the WP_UnitTestCase_Base
class:
::reflect_and_invoke( object $obj, string $method, mixed ...$args ) : mixed
- Reflects a
private
orprotected
method and invokes it. - Returns the method's return value.
::reflect_and_get_value( object $obj, string $property ) : mixed
- Reflects a
private
orprotected
property and gets its value. - Returns the property's value.
::reflect_and_set_value() : mixed
- Reflects a
private
orprotected
property and sets its value. - Returns the previous value for convenient resetting.
While this means the creation of new Reflection<Method|Property>
objects and two calls to ::setAccessible()
for each, I think that this is worthwhile as it helps us have a more robust test suite, with less code to write in test methods. Plus, we could also explore possible caching in future.
Change History (11)
This ticket was mentioned in PR #5338 on WordPress/wordpress-develop by @costdev.
15 months ago
#2
- Keywords has-unit-tests added
#5
@
14 months ago
- Milestone changed from 6.4 to 6.5
Moving to 6.5, as 6.4 RC1 is in a few hours and then will be branched.
#6
@
11 months ago
Some initial thoughts about the proposal (not yet at the PR implementation):
I support adding this test API. Make it easier for contributors while keeping consistency and resetting values and visibility after testing.
I think it can be improved and possibly extended for things like resetting and helpers for static
.
Naming:
I'm thinking the reflect_and_*
naming could be confusing for contributors. Instead, using the PHP class name could help to align it for better understanding and ease of recognizing its relation to the PHP Reflection API:
get_reflectionproperty_value()
set_reflectionproperty_value()
invoke_reflectionmethod()
Extending:
Statics are handled slightly differently than objects. Might be good to extend the API to include statics such as get_static_reflectionproperty_value()
.
Resetting:
Instantiating and resetting visibility and values with each API method call might get too heavy for the test suite performance. Instead, @costdev have you considered storing the details and then resetting (visibilities and values) at tear_down()
?
#7
@
11 months ago
@hellofromTonya
Naming:
get_reflectionproperty_value()
set_reflectionproperty_value()
invoke_reflectionmethod()
Sounds good to me!
Extending:
Statics are handled slightly differently than objects. Might be good to extend the API to include statics such asget_static_reflectionproperty_value()
Yes this will need some testing and consideration for sure. I agree that it makes sense to include it in this API.
Resetting:
Instantiating and resetting visibility and values with each API method call might get too heavy for the test suite performance. Instead, @costdev have you considered storing the details and then resetting (visibilities and values) attear_down()
?
I did consider this, though I was also mindful of memory usage in the test suite (which is additionally worse when running xDebug and coverage reports, for example), where storing objects would increase memory usage. Based on this, the example implementation in PR 5338 calls unset()
on each ReflectionProperty
/ReflectionMethod
when it's done with them for that call.
I'm not sure whether we're best storing the objects and resetting in tear_down()
when we're definitely done with them to improve timing, or calling unset()
when we're potentially done with them to reduce the chance of memory limits being hit on contributor's local machines. There's a trade-off either way I think.
I do wonder if it's possible that we're considering optimization too early, and we might be best committing an initial API, then iterating. Having this already committed in the Core test suite may make analyzing and optimizing its implementation somewhat easier, without having to bundle the API and optimization in one PR (or more if we test different approaches). What do you think Tonya? Handle it with the initial implementation, or iterate when we have more data?
#10
@
5 months ago
- Milestone changed from 6.6 to 6.7
I'm going to punt this since there has been no discussion during this cycle. I'm moving to Future Release
, but if someone has the availability to own this during an upcoming release, please do move it to a numbered milestone.
@costdev do you think that this would benefit from a proposal on Make Core? Maybe we could drum up some feedback from the community.
Examples
Example 1: Ensure that a
private
property is set when aprivate
method is called.What this would normally look like:
What this could look like:
Example 2: Ensure that a
private
property's value affects a public method's result.What this would normally look like:
What this could look like: