8000 [Test container]When running tests getting private service fails · Issue #28528 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Test container]When running tests getting private service fails #28528

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

Closed
Mpreyzner opened this issue Sep 20, 2018 · 14 comments
Closed

[Test container]When running tests getting private service fails #28528

Mpreyzner opened this issue Sep 20, 2018 · 14 comments

Comments

@Mpreyzner
Copy link

Symfony version(s) affected: 4.1.4, 4.1.3

Description
When running tests getting private service fails
According to this: https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing it should be now possible.

Running bin/phpunit results in:

Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The "App\Service\Service" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.

After changing service visibility it works as intended.

How to reproduce
Run bin/phpunit in repository with BugReproducer - https://github.com/Mpreyzner/symfony_visibility_bug

Possible Solution

Additional context

@nicolas-grekas
Copy link
Member

Only non-removed and non-inlined services can be fetched. If you can't get it in 4.1, it means the service has been removed because it's not used anywhere. So why test it?

@lukaszwit
Copy link
lukaszwit commented Sep 20, 2018

I've got similar issue. And my service is definitely used. According to info from compiler pass my service (the one I would like to fetch from container in test) is not private nor public. Is that ok?

@lukaszwit
Copy link
lukaszwit commented Sep 20, 2018

Is there any way to if service was removed by DI component?
Also @nicolas-grekas what do mean by inline service?

My case is as follows:

  1. I've got repository R that implements interface RInterface.
  2. Interface RInterface is used in service S (type hinted constructor)
  3. Service S is used in controller C (again as typhintet constructor param)

When I run test of repository R

class RTest extends KernelTestCase
{
    /**
     * @test
     */
    public function call_shouldWork()
    {
        self::bootKernel();
        $repository = self::$container->get(RInterface::class);
// ... do something else
}
}

When I call tests with filter RTest error is displayed:
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The "App\Repository\RInterface" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.

When I try to find it in container by typing:
bin/console debug:container RI --env=test
it finds the this service.

I have no idea why it's not working. I think that container should'd remove my repository but it looks like it just happen.

Also @nicolas-grekas I think there is big reason why to test unused service: when I start coding and write skeleton of my class X I want to test it before I use it in any other service. Sometimes it's just an integration test when I use container. In that case testing my not used service before use it (with buggy broken code inside) is not possible without using it elsewhere. It just doesn't fit the flow that code is produced (at least sometimes for some developers)

@mateuszsip
Copy link
Contributor

what do mean by inline service?

Service can be inlined when some conditions are met.
If I understood it correctly, usual scenario is: when service is not public and is injected into one service, then it can be inlined. As you can see in related compiler pass this is more complicated.

Could you share result of bin/console debug:container RI --env=test with us?

By the way: I know it's probably not related, but when you test specific implementation (R) fetch it explicitly from container instead of fetching RInterface - the result will be the same, but you fetch exactly what you want to test.

@nicolas-grekas
Copy link
Member

I'm closing this issue this the behavior matches the expectation for private services (@kejwmen description is correct.)
Testing private (removed or inlined) services is possible using a public alias configured for the test environment only. See symfony/symfony-docs#8097 which describes how to do that.

@DonCallisto
Copy link
Contributor
DonCallisto commented Sep 24, 2018

I don't agree with this: if I'm developing app via TDD (as correctly stated above) this should be retrievable without any alias.

Moreover there always be an "outer" service that you'll use in controllers (for instance) so, pratically, it would be used in my application.
Injecting it in controller, btw, does not seems to work as service will be gone anyway (and I should not be forced to inject it in a "dummy" controller just to grab it from container, like I should not be forced to alias it with public: true as the feature of retrieving it from "special test container" would be useless and never used)

So my question is: what private service can be fetched that isn't an unused service? Can anyone bring a valid example? I'm thinking about this scenario without finding a proper answer.

Any thoughts?

@tecnocat
Copy link
Contributor

Same here, we have a very simply test like this:

    /**
     * @return void
     */
    public function testFindById(): void
    {
        self::bootKernel();
        $dummyRepository = self::$container->get(DummyRepositoryInterface::class);
        $dummy           = $dummyRepository->findById(1);
        $this->assertInstanceOf(Dummy::class, $dummy);
        $this->assertNull($dummy->getDeletedAt());
    }

The test was working well until the last composer update in the last week, something was changed in DI Symfony component...

Using the implementation (DummyRepository) instead of an interface (DummyRepositoryInterface) for the test to working again broken the SOLID principle.

@stof
Copy link
Member
stof commented Oct 16, 2018

@DonCallisto the only way to achieve that would be to disable all the optimization of the container inlining some services. But that would mean that the tests are not running against the same container than the actual logic (and in case of circular object graphs, this can make a huge difference, as some circular references are allowed as long as the cycle does not reach a public service in the meantime (which would prevent reorganizing the instantiation of the whole cyclic graph) for instance.

@DonCallisto
Copy link
Contributor

@stof I know what you mean, but at this point, what are the advantages of using this kind of test container? Only to retrieve directly from container those services? Just asking, your answer is very clear and I strongly agree with you.

@iamhappycoder
Copy link

used anywhere = DI tree starts at a controller

@mnapoli
Copy link
Contributor
mnapoli commented Nov 20, 2018

I just stumbled across this problem myself, trying to test a service that is not used for now in the rest of the codebase (refactoring ongoing, the new service is not used for now but I still want to test it).

@andrecadete
Copy link

Kinda sounds a bit anti-TDDish approach to "not allow" testing Objects simply because they are not in use yet. Any TDD purist will be upset / concerned with this.

@thomaskonrad
Copy link
thomaskonrad commented Mar 30, 2019

Same here. I'm in a situation where I offer a service in a bundle, but the service is not used in the bundle itself. It might or might not be used somewhere else in the project. This means that I cannot test the service in a KernelTestCase independently, because the service will get removed from the container.

Why not make the test container also contain unused services? Or offer a way to add a specific service to the container despite the fact that it's unused in KernelTestCase / WebTestCase?

@xabbuh
Copy link
Member
xabbuh commented Mar 30, 2019

IMO your test case then should reflect the use case you try to test. Thus, register a service in your test environment that uses the service your bundle provides.

rgomezcasas added a commit to CodelyTV/php-ddd-skeleton-deprecated that referenced this issue Aug 7, 2019
We only can test one implementation at time, so, file test repository
has been removed: symfony/symfony#28528

Also added all the related infrastructure to clean the databases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

0