Dariux
Dariux

Reputation: 4253

Symfony 3 dependency injection into behat testing class - Type error: Argument 1 passed to __construct() must be an instance

I have such configuration file:

src/AppBundle/services.yml

#imports:
   # - { resource: '../../app/config/config.yml' }

parameters:
    #laikinas, tikras yra config.yml
    app_url: http://app.guru


services:

    UserManagement:
        class: Tests\AppBundle\SharedCode\UserManagement\UserManagement
        arguments: [%app_url%]

    UserRegistrationContext:
        class: Tests\AppBundle\features\user_registration\bootstrap\UserRegistrationContext
        arguments: ['@UserManagement']

tests/AppBundle/features/user_registration/bootstrap/UserRegistrationContext.php

<?php

namespace Tests\AppBundle\features\user_registration\bootstrap;

use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Tests\AppBundle\SharedCode\UserManagement\UserManagement;

use AppBundle\Controller\UserController;
use Tests\AppBundle\features\BaseContext;

/**
 * Defines application features from the specific context.
 *
 * To run:
 * sudo vendor/behat/behat/bin/behat
 * tests/AppBundle/features/user_registration/user_registration.feature
 * --stop-on-failure
 */
class UserRegistrationContext extends BaseContext implements Context, SnippetAcceptingContext
{
    private $userManagement;

    /**
     * UserRegistrationContext constructor.
     */
    public function __construct(UserManagement $userManagement)
    {
        //$this->userManagement = new UserManagement();
        $this->userManagement = $userManagement;
        parent::__construct();
    }
}

I run behat tests and get an error:

vagrant@php7dev:/shared$ sudo vendor/behat/behat/bin/behat tests/AppBundle/features/user_registration/user_registration.feature

Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Type error: Argument 1 passed to Tests\AppBundle\features\user_registration\bootstrap\UserRegistrationContext::__construct() must be an instance of Tests\AppBundle\SharedCode\UserManagement\UserManagement, none given in /shared/tests/AppBundle/features/user_registration/bootstrap/UserRegistrationContext.php:30
Stack trace:
#0 [internal function]: Tests\AppBundle\features\user_registration\bootstrap\UserRegistrationContext->__construct()
#1 /shared/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php(123): ReflectionClass->newInstance()
#2 /shared/vendor/behat/behat/src/Behat/Behat/Context/ContextFactory.php(80): Behat\Behat\Context\ContextFactory->createInstance(Object(ReflectionClass), Array)
#3 /shared/vendor/behat/behat/src/Behat/Behat/Context/Environment/Handler/ContextEnvironmentHandler.php(104): Behat\Behat\Context\ContextFactory->createContext('Tests\\AppBundle...', Array)
#4 /shared/vendor/behat/behat/src/Behat/Testwork/Environme in /shared/tests/AppBundle/features/user_registration/bootstrap/UserRegistrationContext.php on line 30

We can see that in services.yml I have given the parameter. What is wrong?

For services.yml to be read, as I understand I need exctension class, here it is:

src/AppBundle/DependencyInjection/AppExtension.php

<?php

namespace AppBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;

class AppExtension extends Extension
{
    /**
     * @param array            $configs   configs
     * @param ContainerBuilder $container container
     * @return null
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        // ... you'll load the files here later
        $loader = new YamlFileLoader(
            $container,
            new FileLocator(__DIR__ . '/../')
        );
        $loader->load('services.yml');
    }
}

Upvotes: 0

Views: 1373

Answers (1)

Dariux
Dariux

Reputation: 4253

Thank you so much Matteo, so awesome, from his comment alone was able to fix the problem.

It turns out that I had to use Symfony2 Extension and configure dependencies in behat.yml instead of my config. Here is how behat.yml looks now:

default:
  autoload:
   '': %paths.base%/tests/AppBundle/features/user_registration/bootstrap
  formatters:
      progress: ~
  suites:
    app_features:

       paths: [ %paths.base%//tests/AppBundle/features ]
       contexts:
         - Tests\AppBundle\features\user_registration\bootstrap\UserRegistrationContext:
            userManagement: '@UserManagement'
         - Tests\AppBundle\features\user_login\bootstrap\UserLoginContext:
            userManagement: '@UserManagement'
         - Tests\AppBundle\features\password_reset\bootstrap\PasswordResetContext:
            userManagement: '@UserManagement'
  extensions:
    Behat\Symfony2Extension: ~

And I even commented out UserManagement from src/AppBundle/services.yml and it finds it somehow, I do not understand how actually.

And here is something written about this, I googled again for symfony3 behat dependency injection after I solved the problem: http://docs.behat.org/en/v3.0/cookbooks/1.symfony2_integration.html

I remember I saw this page before, but this was not in my head when solving this problem. Maybe because in the example there was Session being injected which is symfony component, while UserManagement class was my created component.

Update:

Done from scratch and will give minimal versions of files how they look:

behat.yml has to be in the root of the project. http://docs.behat.org/en/v3.0/cookbooks/1.symfony2_integration.html

default:
    suites:
        default:
            contexts:
                - FeatureContext:
                    userRepository: "@user_repository"
    extensions:
        Behat\Symfony2Extension: ~

features/bootstrap/FeatureContext.php

use AppBundle\Repository\UserRepository;
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;

/**
 * Defines application features from the specific context.
 */
class FeatureContext implements Context, SnippetAcceptingContext
{
    private $userRepository;

    /**
     * Initializes context.
     *
     * Every scenario gets its own context instance.
     * You can also pass arbitrary arguments to the
     * context constructor through behat.yml.
     */
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    //other methods
}

Pay attention that there has to be matches of keys - if there is such key:

userRepository: "@user_repository"

then in constructor the variable has to be named

$userRepository

Upvotes: 0

Related Questions