Problem/Motivation
There are two invalid tests in core/modules/migrate/tests/src/Unit/MigrateSourceTest.php. Both testCount() and testCountCacheKey() contain the following code:
// Mock the cache to validate set() receives appropriate arguments.
$container = new ContainerBuilder();
$cache = $this->createMock(CacheBackendInterface::class);
$cache->expects($this->any())->method('set')
->with($this->isString(), $this->isInt(), $this->isInt());
$container->set('cache.migrate', $cache);
\Drupal::setContainer($container);
Per the comment at the top, the intent is to validate that a cache entry is set with particular arguments. But the expectation for the set() function is any(), which implicitly means that it's valid if set() is never called. As it turns out, that's what happens.
The problem is that the functions call getSource() (in one case multiple times) which creates a container with a different cache service. This overrides what the test functions are trying to do.
This was discovered in the course of working on #3569422: Convert expectation-less test mocks to stubs - Migrate modules where calls to any() are being removed.
Steps to reproduce
Change the any() to atLeastOnce() and run the tests. The tests will throw errors because set() is never called.
Proposed resolution
- getSource() will continue to set a default stubbed cache service.
- The functions that want to validate a specific interaction with the cache will now override the service with a mock object.
- Move the assertions for checking that the cache is being set to the right place within the test functions.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
Issue fork drupal-3572915
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3572915-invalid-tests
changes, plain diff MR !14754
Comments
Comment #2
dcam commentedComment #3
dcam commentedComment #4
benjifisherIt is almost bed time, so I cannot think hard about this ...
Maybe it is just an inaccurate comment: replace "receives appropriate arguments" with "does not receive inappropriate arguments".
Those code blocks are pretty old (2015 and 2016). It would not be surprising if the tested code has changed since the tests were added.
For the record, it looks like most of the two code blocks in question was added in these issues:
Comment #5
dcam commentedI haven't been able to figure it out yet.
set()should be called during both tests. That code path works when the source is configured with['cache_counts' => TRUE].SourcePluginBase::getCache()is called and returns the mockedCacheBackendInterface. I thought maybe thewith()statement was causing the problem.set()only takes two parameters, but thewith()attempts to set three. But removing it didn't do any good. The test still failed when theany()was changed toatLeastOnce().Comment #6
dcam commentedOh, of course. The tests create a container with a cache service. But then they call
getSource()multiple times which creates a different container with a different cache on each call.Comment #8
dcam commentedThe key was to move the assertion for checking that the cache is being set to the right place within the test functions.
getSource()will continue to set a default stubbed cache service. The functions that want to validate a specific interaction with the cache will now override the service with a mock object.Comment #9
dcam commentedComment #10
smustgrave commentedFrom what I can tell this is a good update, specifically looking at
$cache->expects($this->once())which proves the set.Going to go on a limb
Comment #11
longwaveGood news that the stub conversion is picking up these sorts of errors that we would not spot otherwise, it is worth the pain in the long run!
Committed and pushed b607c8697db to main and 8486ae5d24a to 11.x. Thanks!