zenstruck / browser

A fluent interface for your Symfony functional tests.
MIT License
186 stars 17 forks source link

Build paths from routes in browser #96

Closed benr77 closed 2 years ago

benr77 commented 2 years ago

It is considered best practice to hard-code the URL paths into your tests, so that if a route is modified, the test fails. This ensures a consistent user experience, is good for SEO etc. However, this only really applies to public website type apps.

I build private, Saas type apps, where the URL changing is no big deal. I find it much easier to work with route names, which are defined as constants in the controller classes.

The browser has methods which require the path to test against - e.g.

I think it would be handy to have a matching set of methods, that support passing a route name and parameters instead of the path:

Is this something that could be incorporated into this package, or something better implemented by my app extending things?

If extending things is the way to go, where can I inject the router service?

Thanks

kbond commented 2 years ago

Hey @benr77!

I'm sort of thinking this shouldn't be part of this package for the reason you describe above (It is considered best practice to hard-code the URL paths into your tests). I wouldn't want new users (building a standard app) using ->visitRoute() instead of ->visit().

Of course, if this is a common need by the community, than I'd reconsider.

Here's how you can add to your custom browser:

class AppBrowser extends KernelBrowser
{
    public function visitRoute(string $name, array $parameters = []): self
    {
        return $this->visit($this->client()->getContainer()->get('router')->generate($name, $parameters));
    }

    public function assertOnRoute(string $name, array $parameters = []): self
    {
        // this, I think, is the only way to access this data
        return $this->use(function(RequestDataCollector $collector) use ($name, $parameters) {
            Assert::that($name)->is($collector->getRoute());
            Assert::that($parameters)->is($collector->getRouteParams());
        });
    }
}

Usage in a test:

$this->browser()
    ->withProfiling() // required as we use the request data collector for assertOnRoute
    ->visitRoute('route_name', ['param' => 'value'])
    ->assertOnRoute('route_name', ['param' => 'value'])
kbond commented 2 years ago

Refined assertOnRoute a bit so it doesn't require the data collector:

final public function assertOnRoute(string $name, array $parameters = []): self
{
    $expected = $this->client()->getContainer()->get('router')->generate($name, $parameters, UrlGeneratorInterface::ABSOLUTE_URL);

    Assert::that($this->client()->getRequest()->getUri())->is($expected);

    return $this;
}
benr77 commented 2 years ago

Thanks Kevin - I totally agree with you about not including it in the package.

I have implemented the custom browser as you suggest and it's working nicely.

Thanks for being so helpful as always.