Browse Source

Fix adding mock service to container if not found (#16703)

Fix mocking services that are provided by runtime containers
like `ReflectionContainer()`.
Chris Nizzardini 3 years ago
parent
commit
848efbcf9d

+ 6 - 1
src/Core/TestSuite/ContainerStubTrait.php

@@ -19,6 +19,7 @@ use Cake\Core\Configure;
 use Cake\Core\ContainerInterface;
 use Cake\Event\EventInterface;
 use Closure;
+use League\Container\Exception\NotFoundException;
 use LogicException;
 
 /**
@@ -144,7 +145,11 @@ trait ContainerStubTrait
         }
         foreach ($this->containerServices as $key => $factory) {
             if ($container->has($key)) {
-                $container->extend($key)->setConcrete($factory);
+                try {
+                    $container->extend($key)->setConcrete($factory);
+                } catch (NotFoundException $e) {
+                    $container->add($key, $factory);
+                }
             } else {
                 $container->add($key, $factory);
             }

+ 14 - 0
tests/TestCase/TestSuite/IntegrationTestTraitTest.php

@@ -37,6 +37,7 @@ use LogicException;
 use OutOfBoundsException;
 use PHPUnit\Framework\AssertionFailedError;
 use stdClass;
+use TestApp\ReflectionDependency;
 
 /**
  * Self test of the IntegrationTestTrait
@@ -1695,6 +1696,19 @@ class IntegrationTestTraitTest extends TestCase
     /**
      * Test that mockService() injects into controllers.
      */
+    public function testHandleWithMockServicesFromReflectionContainer(): void
+    {
+        $this->mockService(ReflectionDependency::class, function () {
+            return new ReflectionDependency();
+        });
+        $this->get('/dependencies/reflectionDep');
+        $this->assertResponseOk();
+        $this->assertResponseContains('{"dep":{}}', 'Contains the data from the reflection container');
+    }
+
+    /**
+     * Test that mockService() injects into controllers.
+     */
     public function testHandleWithMockServicesOverwrite(): void
     {
         $this->mockService(stdClass::class, function () {

+ 4 - 0
tests/test_app/TestApp/Application.php

@@ -17,6 +17,7 @@ declare(strict_types=1);
 namespace TestApp;
 
 use Cake\Console\CommandCollection;
+use Cake\Controller\ComponentRegistry;
 use Cake\Core\Configure;
 use Cake\Core\ContainerInterface;
 use Cake\Error\Middleware\ErrorHandlerMiddleware;
@@ -25,6 +26,7 @@ use Cake\Http\MiddlewareQueue;
 use Cake\Routing\Exception\DuplicateNamedRouteException;
 use Cake\Routing\Middleware\RoutingMiddleware;
 use Cake\Routing\RouteBuilder;
+use League\Container\ReflectionContainer;
 use stdClass;
 use TestApp\Command\AbortCommand;
 use TestApp\Command\DependencyCommand;
@@ -96,5 +98,7 @@ class Application extends BaseApplication
         $container->add(stdClass::class, json_decode('{"key":"value"}'));
         $container->add(DependencyCommand::class)
             ->addArgument(stdClass::class);
+        $container->add(ComponentRegistry::class);
+        $container->delegate(new ReflectionContainer());
     }
 }

+ 9 - 0
tests/test_app/TestApp/Controller/DependenciesController.php

@@ -71,6 +71,15 @@ class DependenciesController extends Controller
     }
 
     /**
+     * @param \TestApp\ReflectionDependency $dep
+     * @return \Cake\Http\Response
+     */
+    public function reflectionDep(\TestApp\ReflectionDependency $dep)
+    {
+        return $this->response->withStringBody(json_encode(compact('dep')));
+    }
+
+    /**
      * @param mixed $any
      * @return \Cake\Http\Response
      */

+ 11 - 0
tests/test_app/TestApp/ReflectionDependency.php

@@ -0,0 +1,11 @@
+<?php
+declare(strict_types=1);
+
+namespace TestApp;
+
+class ReflectionDependency
+{
+    public function __construct()
+    {
+    }
+}