SymfonyExtension
SymfonyExtension copied to clipboard
using "Behatch\Context\RestContext" cause The service has a dependency on a non-existent service "behatch.http_call.request"
Hey guys, Thanks for your awesome work. I'm trying to use Behatch RestContext in my context but I get the following error:
The service "App\Tests\Behat\FeatureContext" has a dependency on a non-existent service "behatch.http_call.request".
Am I wrong somewhere?
This is my configuration: symfony: 4.4 composer.json:
"require-dev": {
"behat/behat": "^3.6",
"behat/mink": "^1.7@dev",
"behat/mink-browserkit-driver": "^1.3",
"behat/mink-extension": "^2.3",
"behatch/contexts": "^3.3",
"coduo/php-matcher": "^4.0",
"doctrine/doctrine-fixtures-bundle": "^3.3",
"friends-of-behat/symfony-extension": "^2.0.0",
"fzaninotto/faker": "^1.9",
"symfony/debug-pack": "*",
"symfony/maker-bundle": "^1.0",
"symfony/profiler-pack": "*",
"symfony/test-pack": "*"
}
behat.yml
default:
suites:
default:
contexts:
- Behat\MinkExtension\Context\MinkContext
- behatch:context:browser
- behatch:context:debug
- behatch:context:system
- behatch:context:json
- behatch:context:table
- behatch:context:rest
- behatch:context:xml
extensions:
FriendsOfBehat\SymfonyExtension: ~
Behatch\Extension: ~
Behat\MinkExtension:
base_url: 'http://localhost:8000'
sessions:
symfony:
symfony: ~
service_test.yml
services:
_defaults:
autowire: true
autoconfigure: true
App\Tests\Behat\FeatureContext:
public: true
arguments:
- '@behatch.http_call.request'
- '@App\DataFixtures\AppFixtures'
- '@doctrine.orm.default_entity_manager'
# App\Tests\Behat\:
# resource: '../tests/Behat/*'
app/tests/Behat/FeatureContext.php
<?php
namespace App\Tests\Behat;
use Behatch\Context\RestContext;
use Coduo\PHPMatcher\Factory\SimpleFactory;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;
/**
* This context class contains the definitions of the steps used by the demo
* feature file. Learn how to get started with Behat and BDD on Behat's website.
*
* @see http://behat.org/en/latest/quick_start.html
*/
final class FeatureContext extends RestContext
{
/**
* @var \App\DataFixtures\AppFixtures
*/
private $fixtures;
/**
* @var \Coduo\PHPMatcher\Matcher
*/
private $matcher;
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $em;
public function __construct(
\Behatch\HttpCall\Request $request,
\App\DataFixtures\AppFixtures $fixtures,
\Doctrine\ORM\EntityManagerInterface $em
) {
parent::__construct($request);
$this->fixtures = $fixtures;
$this->matcher = (new SimpleFactory())->createMatcher();
$this->em = $em;
}
/**
* @BeforeScenario @createSchema
*/
public function createSchema(){
// .....
}
}
I have the same problem.
As a hotfix I added in my services_test.yaml
manual configuration:
behatch.http_call.request:
class: Behatch\HttpCall\Request
arguments:
- '@behat.mink'
public: false
Original path with configuration: behatch/contexts/src/Resources/services/http_call.yml
I have the same issue. The problem is that behatch and symfony-extension have two way to inject dependencies.
You can inject Behatch dependencies through behat.yml like this:
- App\Behat\Context\JsonExtendedContext:
httpCallResultPool: 'behatch:behatch.http_call.result_pool'
request: 'behatch:behatch.http_call.request'
Because Behatch provide a resolver to behat HttpCallResultPoolResolver
But symfony-extension only provide dependency injection for context as service... so: in behat.yaml this doesn't work (because symfony don't provide resolver to behat)
- App\Behat\Context\JsonExtendedContext:
httpCallResultPool: 'behatch:behatch.http_call.result_pool'
request: 'behatch:behatch.http_call.request'
kernel: "@kernel"
in service_test.yaml this doesn't work (because behatch service are private and can only be injected through behat Resolver
App\Behat\Context\JsonExtendedContext:
$httpCallResultPool: "@behatch.http_call.result_pool"
$kernel: "@kernel"
@GrzegorzMatuszakTSH I'm not sure your solution is the best, because you provide a new reference of this object, I'm not sure that internally behatch will use the same instance. So between your context and behatch context, informations will be differents.
This is a real issue.
I think there is two way to handle this:
- Provide a behat Resolver to inject symfony objects through behat.yaml
- In symfony-extension extension class trying to resolve arguments with every resolver that implement
Behat\Behat\Context\Argument\ArgumentResolver
(I think this solution is the best, it will allow full autowiring)
@CharloMez Yes, I agree with you. As I wrote my "solution" was a hotfix and we need a change in the library to fix it in the right way. Your ideas are very good :)
@CharloMez would you be willing to contribute the second solution?
@pamil I'm trying, but I don't find how to get all ArgumentResolver... when I try in compiler pass
use Behat\Behat\Context\ServiceContainer\ContextExtension;
.....
$container->findTaggedServiceIds(ContextExtension::ARGUMENT_RESOLVER_TAG);
I got nothing, but behat find them in it's extension here https://github.com/Behat/Behat/blob/98cfd077dcb68a6f1e20f3faa94b31e63b0dd182/src/Behat/Behat/Context/ServiceContainer/ContextExtension.php#L279
I think I miss something in symfony process. It is probably not the same container which is injected in behat extension, but I don't know how to access to the same container from SymfonyExtension. any idea how to get these services ?
I tried many other solutions (extending JsonContext, calling the behatch internal container to get the JsonContext, ...) but only one worked (that's dirty though): Reflection
<?php
namespace App\Tests\Behat;
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behatch\Context\JsonContext;
use Behatch\HttpCall\HttpCallResultPool;
use Behatch\Json\JsonInspector;
class JsonExtendedContext implements Context
{
private HttpCallResultPool $httpCallResultPool;
private JsonInspector $inspector;
/**
* @BeforeScenario
*/
public function beforeScenario(BeforeScenarioScope $scope)
{
$jsonContext = $scope->getEnvironment()->getContext(JsonContext::class);
$refl = new \ReflectionClass(JsonContext::class);
$httpCallResultPoolProperty = $refl->getProperty('httpCallResultPool');
$inspectorProperty = $refl->getProperty('inspector');
$httpCallResultPoolProperty->setAccessible(true);
$inspectorProperty->setAccessible(true);
$this->httpCallResultPool = $httpCallResultPoolProperty->getValue($jsonContext);
$this->inspector = $inspectorProperty->getValue($jsonContext);
}
// your steps here ...
Given the example from the original @masoud91 comment, it seems you are only using the Request
to forward it to the parent __constructor
, you can simply avoid to extend the RestContext
and only implement Behat\Behat\Context\Context
.
However this is not resolving the issue of getting the Request
for ourselves.
@pamil I'm happy to contribute the second solution if this is still needed, I implemented an extension on top of the original sf2 extension and it doesn't work with this one, so if you give me some guidance I can contribute.
I managed to wrangle a working version by doing this:
services_test.yaml
:
services:
defaults:
autowire: true
autoconfigure: true
bind:
$behatContainer: '@test.service_container'
App\Tests\Context\:
resource: '../tests/Context/*'
public: true
JsonContext.php
:
class JsonContext extends BaseContext
{
private ContainerInterface $behatContainer;
public function __construct(ContainerInterface $behatContainer, string $evaluationMode = 'javascript')
{
$this->behatContainer = $behatContainer;
}
/** @BeforeScenario */
public function gatherContexts(BeforeScenarioScope $scope): void
{
$container = $this->behatContainer->get('behat.service_container');
$this->httpCallResultPool = $container->get('behatch.http_call.result_pool');
}
}
However, using this method I couldn't get a working version to override RestContext
. I think this is because the request isn't actually put into the container, so there's no method to get the current Behat request. Definitely requires more work.
For other services, here's the list of available services that I found in the container:
{
"cli.input": "Symfony\\Component\\Console\\Input\\ArgvInput",
"cli.output": "Symfony\\Component\\Console\\Output\\ConsoleOutput",
"translator": "Behat\\Behat\\Definition\\Translator\\Translator",
"fob_symfony.kernel": "App\\Kernel",
"fob_symfony.mink": "FriendsOfBehat\\SymfonyExtension\\Mink\\Mink",
"fob_symfony.driver_kernel": "App\\Kernel",
"behatch.http_call.result_pool": "Behatch\\HttpCall\\HttpCallResultPool",
"definition.pattern_transformer": "Behat\\Behat\\Definition\\Pattern\\PatternTransformer",
"environment.manager": "Behat\\Testwork\\Environment\\EnvironmentManager",
"argument.mixed_organiser": "Behat\\Testwork\\Argument\\MixedArgumentOrganiser",
"call.center": "Behat\\Testwork\\Call\\CallCenter",
"cli.command": "Behat\\Testwork\\Cli\\Command"
}