SymfonyExtension icon indicating copy to clipboard operation
SymfonyExtension copied to clipboard

using "Behatch\Context\RestContext" cause The service has a dependency on a non-existent service "behatch.http_call.request"

Open masoud91 opened this issue 4 years ago • 9 comments

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(){
        // .....
    }
}

masoud91 avatar Mar 07 '20 08:03 masoud91

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

GrzegorzMatuszakTSH avatar Mar 11 '20 07:03 GrzegorzMatuszakTSH

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 avatar May 05 '20 10:05 CharloMez

@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 :)

GrzegorzMatuszakTSH avatar May 05 '20 10:05 GrzegorzMatuszakTSH

@CharloMez would you be willing to contribute the second solution?

pamil avatar May 05 '20 13:05 pamil

@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 ?

CharloMez avatar May 06 '20 11:05 CharloMez

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 ...

ymarillet avatar Jul 01 '20 10:07 ymarillet

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.

aleksbrgt avatar Jul 31 '20 08:07 aleksbrgt

@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.

gonzalovilaseca avatar Sep 30 '20 07:09 gonzalovilaseca

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"
}

nathansalter avatar Jan 07 '21 11:01 nathansalter