Configuring Test Suites

We already talked about configuring multiple contexts for a single testsuite in aprevious chapter. Now it istime to talk about test suites themselves. A test suite represents a group ofconcrete features together with the information on how to test them.

With suites you can configure Behat to test different kinds of featuresusing different kinds of contexts and doing so in one run. Test suites arereally powerful andbehat.yml makes them that much more powerful:

<?php// behat.phpuseApp\Tests\Behat\Context\AdminContext;useApp\Tests\Behat\Context\CoreDomainContext;useApp\Tests\Behat\Context\UserContext;useBehat\Config\Config;useBehat\Config\Config\Filter\RoleFilter;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('core_features')->withPaths('%paths.base%/features/core')->withContexts(CoreDomainContext::class))->withSuite(newSuite('user_features')->withFilter(newRoleFilter('user'))->withPaths('%paths.base%/features/web')->withContexts(UserContext::class))->withSuite(newSuite('admin_features')->withFilter(newRoleFilter('admin'))->withPaths('%paths.base%/features/web')->withContexts(AdminContext::class));returnnewConfig()->withProfile($profile);

Note

On PHP < 8.4, you need to wrap new invocations with parentheses before calling config object methods.

// ...// $profile = new Profile('default')$profile=(newProfile('default'))->withSuite(// new Suite('core_features')(newSuite('core_features'))->withPaths('%paths.base%/features/core')->withContexts(CoreDomainContext::class))// ...

Suite Paths

One of the most obvious settings of the suites is thepathsconfiguration:

<?php// behat.phpuseBehat\Config\Config;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('core_features')->withPaths('%paths.base%/features','%paths.base%/test/features','%paths.base%/vendor/.../features',));return(newConfig())->withProfile($profile);

As you might imagine, this option tells Behat where to search for test features.You could, for example, tell Behat to look into thefeatures/web folder for features and test them withWebContext:

<?php// behat.phpuseApp\Tests\Behat\Context\WebContext;useBehat\Config\Config;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('web_features')->withPaths('%paths.base%/features/web')->withContexts(WebContext::class));returnnewConfig()->withProfile($profile);

You then might want to also describe some API-specific features infeatures/api and test them with an API-specificApiContext. Easy:

<?php// behat.phpuseApp\Tests\Behat\Context\ApiContext;useApp\Tests\Behat\Context\WebContext;useBehat\Config\Config;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('web_features')->withPaths('%paths.base%/features/web')->withContexts(WebContext::class))->withSuite(newSuite('api_features')->withPaths('%paths.base%/features/api')->withContexts(ApiContext::class));returnnewConfig()->withProfile($profile);

This will cause Behat to:

  1. Find all features insidefeatures/web and test them using yourWebContext.

  2. Find all features insidefeatures/api and test them using yourApiContext.

Note

%paths.base% is a special variable inbehat.yml that refersto the folder in whichbehat.yml is stored. When using it, orany other percent-encased variable, it has to be put in quotes.

Path-based suites are an easy way to test highly-modular applicationswhere features are delivered by highly decoupled components. With suitesyou can test all of them together.

Suite Filters

In addition to being able to run features from different directories,we can run scenarios from the same directory, but filtered by specificcriteria. The Gherkin parser comes bundled with a set of cool filterssuch astags andname filters. You can use these filters to runfeatures with specific tag (or name) in specific contexts:

<?php// behat.phpuseApp\Tests\Behat\Context\ApiContext;useApp\Tests\Behat\Context\WebContext;useBehat\Config\Config;useBehat\Config\Filter\TagFilter;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('web_features')->withFilter(newTagFilter('@web'))->withPaths('%paths.base%/features')->withContexts(WebContext::class))->withSuite(newSuite('api_features')->withFilter(newTagFilter('@api'))->withPaths('%paths.base%/features')->withContexts(ApiContext::class));returnnewConfig()->withProfile($profile);

This configuration will tell Behat to run features and scenariostagged as@web inWebContext and features and scenariostagged as@api inApiContext. Even if they all are storedin the same folder. How cool is that? But it gets even better,because Gherkin 4+ (used in Behat 3+) added a very specialrolefilter. That means, you can now have nice actor-based suites:

<?php// behat.phpuseApp\Tests\Behat\Context\AdminContext;useApp\Tests\Behat\Context\UserContext;useBehat\Config\Config;useBehat\Config\Filter\RoleFilter;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('user_features')->withFilter(newRoleFilter('user'))->withPaths('%paths.base%/features')->withContexts(UserContext::class))->withSuite(newSuite('api_features')->withFilter(newRoleFilter('admin'))->withPaths('%paths.base%/features')->withContexts(AdminContext::class));return(newConfig())->withProfile($profile);

A Role filter takes a look into the feature description block:

Feature: Registering users  In order to help more people use our system  As an admin  I need to be able to register more users

It looks for aAsa... orAsan... pattern and guesses itsactor from it. It then filters features that do not have the expectedactor from the set. In the case of our example, it basically means thatfeatures described from the perspective of theuser actor willbe tested inUserContext and features described from theperspective of theadmin actor will be tested inAdminContext.Even if they are in the same folder.

While it is possible to specify filters as part of suite configuration,sometimes you will want to exclude certain scenarios across the suite, with theoption to override the filters at the command line.

This is achieved by specifying the filter in the gherkin configuration:

<?php// behat.phpuseBehat\Config\Config;useBehat\Config\Filter\TagFilter;useBehat\Config\Profile;$profile=newProfile('default')->withFilter(newTagFilter('~@wip'));returnnewConfig()->withProfile($profile);

In this instance, scenarios tagged as @wip will be ignored unless the CLIcommand is run with a custom filter, e.g.:

vendor/bin/behat--tags=wip

Tip

More details on identifying tests can be found in the chapterIdentifying Tests.

Suite Contexts

Being able to specify a set of features with a set of contexts forthese features inside the suite has a very interesting side-effect.You can specify the same features in two different suites being testedagainst different contextsor the same contexts configured differently.This basically means that you can use the same subset of features todevelop different layers of your application with Behat:

<?php// behat.phpuseApp\Tests\Behat\Context\DomainContext;useApp\Tests\Behat\Context\WebContext;useBehat\Config\Config;useBehat\Config\Filter\TagFilter;useBehat\Config\Profile;useBehat\Config\Suite;$profile=newProfile('default')->withSuite(newSuite('domain_features')->withPaths('%paths.base%/features')->withContexts(DomainContext::class))->withSuite(newSuite('web_features')->withFilter(newTagFilter('@web'))->withPaths('%paths.base%/features')->withContexts(WebContext::class));returnnewConfig()->withProfile($profile);

In this case, Behat will first run all the features from thefeatures/folder inDomainContext and then only those tagged with@web inWebContext.

Tip

It might be worth reading how toexecute a specificsuite orinitialize a newsuite