Add more sanity checks

This commit is contained in:
Kamil Kokot
2019-02-12 23:42:41 +01:00
parent 9e9529c320
commit d74cd251d5
4 changed files with 120 additions and 34 deletions

View File

@@ -0,0 +1,56 @@
Feature: Context constructor dependency injection compatibility
Scenario: Using context consturctor dependency injection
Given a working Symfony application with SymfonyExtension configured
And a Behat configuration containing:
"""
default:
suites:
default:
contexts:
- App\Tests\SomeContext:
- "@App\\Foo"
services:
App\Foo: ~
"""
And a class file "src/Foo.php" containing:
"""
<?php
namespace App;
final class Foo
{
}
"""
And a feature file containing:
"""
Feature:
Scenario:
Then it should pass
"""
And a context file "tests/SomeContext.php" containing:
"""
<?php
namespace App\Tests;
use App\Foo;
use Behat\Behat\Context\Context;
final class SomeContext implements Context {
public function __construct(Foo $foo)
{
$this->foo = $foo;
}
/** @Then it should pass */
public function itShouldPass(): void
{
assert($this->foo instanceof Foo);
}
}
"""
When I run Behat
Then it should pass

View File

@@ -13,9 +13,13 @@ declare(strict_types=1);
namespace FriendsOfBehat\SymfonyExtension\Context\Environment\Handler;
use Behat\Behat\Context\Argument\ArgumentResolverFactory;
use Behat\Behat\Context\Argument\NullFactory;
use Behat\Behat\Context\Argument\SuiteScopedResolverFactory;
use Behat\Behat\Context\Argument\SuiteScopedResolverFactoryAdapter;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\ContextClass\ClassResolver;
use Behat\Behat\Context\Initializer\ContextInitializer;
use Behat\Behat\Context\ContextFactory;
use Behat\Testwork\Environment\Environment;
use Behat\Testwork\Environment\Exception\EnvironmentIsolationException;
use Behat\Testwork\Environment\Handler\EnvironmentHandler;
@@ -32,15 +36,29 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
/** @var KernelInterface */
private $symfonyKernel;
/** @var ContextInitializer[] */
private $contextInitializers = [];
/** @var ClassResolver[] */
private $classResolvers = [];
public function __construct(KernelInterface $symfonyKernel)
/** @var ContextFactory */
private $contextFactory;
/** @var ArgumentResolverFactory */
private $resolverFactory;
/**
* @param ArgumentResolverFactory|SuiteScopedResolverFactory $resolverFactory
*/
public function __construct(KernelInterface $symfonyKernel, ContextFactory $factory, $resolverFactory = null)
{
$this->symfonyKernel = $symfonyKernel;
$this->contextFactory = $factory;
if ($resolverFactory && !$resolverFactory instanceof ArgumentResolverFactory) {
$resolverFactory = new SuiteScopedResolverFactoryAdapter($resolverFactory);
}
$this->resolverFactory = $resolverFactory ?: new NullFactory();
}
public function supportsSuite(Suite $suite): bool
@@ -51,8 +69,8 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
public function buildEnvironment(Suite $suite): Environment
{
$environment = new UninitialisedContextServiceEnvironment($suite);
foreach ($this->getSuiteContextsServices($suite) as $contextId) {
$environment->registerContextService($contextId, $this->getContextClass($contextId));
foreach ($this->getSuiteContextsServices($suite) as [$contextId, $contextArguments]) {
$environment->registerContextService($contextId, $this->getContextClass($contextId), $contextArguments);
}
return $environment;
@@ -73,28 +91,24 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
$this->assertEnvironmentCanBeIsolated($uninitializedEnvironment, $testSubject);
$environment = new InitialisedContextServiceEnvironment($uninitializedEnvironment->getSuite());
foreach ($uninitializedEnvironment->getContextServices() as $contextId) {
$resolvers = $this->resolverFactory->createArgumentResolvers($environment);
foreach ($uninitializedEnvironment->getContextServicesWithArguments() as $contextId => $arguments) {
/** @var Context $context */
$context = $this->getContext($contextId);
$this->initializeInstance($context);
$context = $this->getContext($contextId, $arguments, $resolvers);
$environment->registerContext($context);
}
return $environment;
}
public function registerContextInitializer(ContextInitializer $contextInitializer): void
{
$this->contextInitializers[] = $contextInitializer;
}
public function registerClassResolver(ClassResolver $classResolver): void
{
$this->classResolvers[] = $classResolver;
}
/**
* @return string[]
* @return array[]
*
* @throws SuiteConfigurationException If "contexts" setting is not an array
*/
@@ -110,7 +124,20 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
), $suite->getName());
}
return $contextsServices;
return array_map(
function ($context): array {
$class = $context;
$arguments = [];
if (is_array($context)) {
$class = current(array_keys($context));
$arguments = $context[$class];
}
return [$class, $arguments];
},
$contextsServices
);
}
/**
@@ -127,13 +154,6 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
}
}
private function initializeInstance(Context $context): void
{
foreach ($this->contextInitializers as $initializer) {
$initializer->initializeContext($context);
}
}
private function resolveContextId(string $contextId): string
{
foreach ($this->classResolvers as $resolver) {
@@ -162,7 +182,7 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
throw new \DomainException(sprintf('There is no service or class "%s".', $contextId));
}
private function getContext(string $contextId): Context
private function getContext(string $contextId, array $arguments = [], array $resolvers = []): Context
{
$contextId = $this->resolveContextId($contextId);
@@ -171,7 +191,7 @@ final class ContextServiceEnvironmentHandler implements EnvironmentHandler
if ($this->getContainer()->has($contextId)) {
$context = $this->getContainer()->get($contextId);
} elseif (class_exists($class)) {
$context = new $class();
$context = $this->contextFactory->createContext($class, $arguments, $resolvers);
} else {
throw new \DomainException(sprintf('There is no service or class "%s".', $contextId));
}

View File

@@ -24,9 +24,12 @@ final class UninitialisedContextServiceEnvironment extends StaticEnvironment imp
/** @var string[] */
private $contextServices = [];
public function registerContextService(string $serviceId, string $serviceClass): void
public function registerContextService(string $serviceId, string $serviceClass, array $arguments = []): void
{
$this->contextServices[$serviceId] = $serviceClass;
$this->contextServices[$serviceId] = [
'class' => $serviceClass,
'arguments' => $arguments,
];
}
public function getContextServices(): array
@@ -41,11 +44,20 @@ final class UninitialisedContextServiceEnvironment extends StaticEnvironment imp
public function getContextClasses(): array
{
return array_values($this->contextServices);
return array_map(function (array $contextDetails): string {
return $contextDetails['class'];
}, $this->contextServices);
}
public function hasContextClass($class): bool
{
return in_array($class, $this->contextServices, true);
return in_array($class, $this->getContextClasses(), true);
}
public function getContextServicesWithArguments(): iterable
{
foreach ($this->contextServices as $contextDetails) {
yield $contextDetails['class'] => $contextDetails['arguments'];
}
}
}

View File

@@ -137,6 +137,8 @@ final class SymfonyExtension implements Extension
{
$definition = new Definition(ContextServiceEnvironmentHandler::class, [
new Reference(self::KERNEL_ID),
new Reference(ContextExtension::FACTORY_ID),
new Reference(ContextExtension::AGGREGATE_RESOLVER_FACTORY_ID)
]);
$definition->addTag(EnvironmentExtension::HANDLER_TAG, ['priority' => 128]);
@@ -250,10 +252,6 @@ final class SymfonyExtension implements Extension
{
$definition = $container->findDefinition('fob_symfony.environment_handler.context_service');
foreach ($container->findTaggedServiceIds(ContextExtension::INITIALIZER_TAG) as $serviceId => $tags) {
$definition->addMethodCall('registerContextInitializer', [$container->getDefinition($serviceId)]);
}
foreach ($container->findTaggedServiceIds(ContextExtension::CLASS_RESOLVER_TAG) as $serviceId => $tags) {
$definition->addMethodCall('registerClassResolver', [$container->getDefinition($serviceId)]);
}