Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

codeception + symfony - New services cannot be grabbed #5828

Closed
elzozo13 opened this issue Jan 23, 2020 · 7 comments · Fixed by Codeception/module-symfony#20
Closed

codeception + symfony - New services cannot be grabbed #5828

elzozo13 opened this issue Jan 23, 2020 · 7 comments · Fixed by Codeception/module-symfony#20

Comments

@elzozo13
Copy link

I have the following setup:

Symfony 5 project, with codeception 4.

I am trying to create a test for a newly created service. The problem is that I receive the error below when I try to test it.

Test  tests/integration/Shop/NewServiceCest.php:testSomething
Step  Grab service "App\Service\NewService"
Fail  Service App\Service\NewService is not available in container

The service code is below (is basically empty):

<?php

namespace App\Service;

class NewService
{

    public function __construct()
    {

    }

}

The test code is below (again, basically empty):

<?php

namespace App\Tests\Integration\New;

use App\Exception\NewException;
use App\Service\NewService;
use App\Tests\IntegrationTester;

class NewServiceCest {

    private NewService $newService;

    public function _before(IntegrationTester $i) {
        $this->newService = $i->grabService(NewService::class);
    }

    public function testSomething(IntegrationTester $i) {
        // $i->expectThrowable(NewException::class, $this->newService->doStuff('invalidArgument'));
    }
}

Integration tests yml:

actor: IntegrationTester
modules:
    enabled:
        - Symfony:
              app_path: 'src'
              environment: 'test'
        - Doctrine2:
              depends: Symfony
              cleanup: true
              dump: 'tests/_data/integration.sql'
              populate: true
              reconnect: true
        - \App\Tests\Helper\Integration
        - Asserts

I have tried to clear cache, recreate it etc. everything I could think of from cli, including (current path is project dir):

$./bin/console c:c
$./bin/console c:c --env=test
$rm -rf var (this should do nothing but still tried it)
$rm -rf tests/_support/_generated
$./bin/console cache:warmup --env=test
$./vendor/bin/codecept build

When I ran the test, same issue.

What did make it work, was to inject the service somewhere (for example in a controller), execute the controller, then use ./vendor/bin/codecept build. After I did those steps the test worked as expected. The obvious problem with this is that I am trying to create a pipeline for deploy where I raise a test environment (clean, on the fly before deploying to servers), run tests, and ensure everything is fine. Injecting all services somewhere just to be able to build is not an option (but I am opened to any cli options, including running most commands).

Even more strange is that after I did this, now the test works as expected even after I clean caches (var and _generated). Is like it would save something in a different place that I can't seem to find (can't find any relevant configs regarding this and google and bing fail me).

So... why is this happening, and how can I reliably make it work?

@Naktibalda
Copy link
Member

I am not an expert in Symfony, but I think that your services could be private: https://symfony.com/doc/current/service_container.html#public-versus-private-services

From Symfony 4.0, every service defined is private by default.

What does this mean? When a service is public, you can access it directly from the container object, which can also be injected thanks to autowiring. This is mostly useful when you want to fetch services lazily:

As a best practice, you should only create private services, which will happen automatically. And also, you should not use the $container->get() method to fetch public services.

But, if you do need to make a service public, override the public setting:

# config/services.yaml
services:
    # ... same code as before

    # explicitly configure the service
    Acme\PublicService:
        public: true

@monotonesol
Copy link

Probably you have some additional configuration for the test env in your Symfony app, by default https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing it should be accessible

@Naktibalda
Copy link
Member

module-symfony uses some special test container, if it is available: Codeception/module-symfony@1d4cdae

@elzozo13
Copy link
Author

elzozo13 commented Mar 7, 2020

I am not an expert in Symfony, but I think that your services could be private: https://symfony.com/doc/current/service_container.html#public-versus-private-services

From Symfony 4.0, every service defined is private by default.
What does this mean? When a service is public, you can access it directly from the container object, which can also be injected thanks to autowiring. This is mostly useful when you want to fetch services lazily:

As a best practice, you should only create private services, which will happen automatically. And also, you should not use the $container->get() method to fetch public services.
But, if you do need to make a service public, override the public setting:

# config/services.yaml
services:
    # ... same code as before

    # explicitly configure the service
    Acme\PublicService:
        public: true

Hello, sorry for delayed answer. Services are not private (if they were private nothing would've fixed it except making them public). The problem seems to be the fact that if the service is not used anywhere (as is the case with first case in ttd methodology) it is not added at all in the container. I worked around the issue by just injecting the service in a dummy command but is... dirty.

ThomasLandauer added a commit to ThomasLandauer/module-symfony that referenced this issue Oct 21, 2020
Adding hint about unused services not being available, see Codeception/Codeception#5828 and https://stackoverflow.com/a/63131476/1668200
@ThomasLandauer
Copy link
Member

As a "final" solution: Would it be possible that Codeception sets every service to public? Or just injects everything in the test container?

@TavoNiievez
Copy link
Member

more information about this.

This bug related to 'all services as public' was described when Symfony 4.1 was released.
In my applications, I was not able to replicate it since -at some point- I added a services_test.yaml file just for the test environment which explicitly makes the services public by default:

# config/services_test.yaml

services:
    _defaults:
        public: true

    # If you need to access services in a test, create an alias
    # and then fetch that alias from the container. As a convention,
    # aliases are prefixed with test. For example:
    #
    # test.App\Intangible\Service\MyService: '@App\Intangible\Service\MyService'

In the same way, the Symfony documentation suggests creating this file and making public the services that are required.
What I'm trying to say is that this 'public services' behavior (or aliases, if that's the case) should be explicitly added by the user...

I agree to add a more descriptive message, like the one suggested by @ThomasLandauer .

ThomasLandauer added a commit to ThomasLandauer/module-symfony that referenced this issue Oct 22, 2020
@ThomasLandauer
Copy link
Member

ThomasLandauer commented Oct 22, 2020

@TavoNiievez I agree. Telling people what do to is enough - probably better than messing with their config ;-)

I updated it to mention _test explicitly: Codeception/module-symfony@38d5932

So once this gets merged, this issue can be closed.

See also Codeception/module-symfony#21

Naktibalda pushed a commit to Codeception/module-symfony that referenced this issue Oct 31, 2020
* Update Symfony.php

Adding hint about unused services not being available, see Codeception/Codeception#5828 and https://stackoverflow.com/a/63131476/1668200

* Update Symfony.php

Switching to *test* config, see Codeception/Codeception#5828 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants