Behat is a behavioural testing suite for PHP. With its Cucumber-style scenarios and PHP step definitions, it is perfect for applying test automation to Behaviour Driven Development. But with changes in the past few years in the way it is typically used, it is becoming an important tool for software design, and driving the structure of the domain.
Behat’s History and UI Level Testing
In years gone by, the typical way to use Behat was as follows:
First, we would gather requirements. Although Modelling by Example (MbE) was not yet invented, we would still generate some sort of feature file, based on conversations with non-technical stakeholders. We would then do an enormous amount of faffing about with xpaths and CSS selectors, while developing a feature, and would try to end up with UI-level tests that asserted the functionality described in the feature files should basically work.
These UI-level tests had the following disadvantages:
- Flakiness – tests based on HTTP queries, waiting for JS to load, etc. tended to fail for basically no reason. Getting through a large UI test suite and ending up with a green build was a real achievement.
- Slow – between the HTTP queries, and often waiting for JS to run in some sort of headless browser, or even Selenium-based browser control, took ages. This loosened the feedback loop to the point where the test suite lost most of its value.
- Fiddliness – changing the name of a CSS class broke all the tests written by PHP devs.
Service Level Testing
As time moved on, the use of Behat in service-level testing increased. Nowadays, in a well-structured (behavioural) test suite, UI tests are the exception, rather than the rule. There is still some value in basic smoke testing for the index file, but most of the behavioural tests are now at the service level. This means that they instantiate objects inline, directly in the Behat step definition. They then test them as dictated by the original scenario.
The objects instantiated would typically have been stateless service classes at the time. These days, they may be stateless service classes, regular domain objects, or some sort of aggregate root object. These subjects under test will often come with their own testing dependency injection configuration, and the use of in-memory fakes for data access objects is common. The Behat step definitions are also typically littered with value objects, with Behat’s ‘transformer’ system providing a hook for automatic conversion from a primitive type in a feature file to a VO.
Behat as a Design Tool
This evolution in the use of Behat has had an interesting side effect: it has become a software design tool. Gone are the days of inside-out design, where the detailed implementation of a particular class was the first thing you thought about. Behat helps the developer take an outside-in approach to design. By forcing the developer to decide on the name and purpose of a domain object or a stateless service class first, and to write down its public API before the class even exists, Behat assists with the software design process. It guides us towards creating packages or modules which are easy to use, and have a sensible public API which relates closely to the domain they model, and in a lot of cases, that results in less mess.
Overall, where Behat is used as a tool for service-level testing, and where those tests are written before the production code, the resulting software architecture tends to be much less of a mess. Having started by teaching use to gather requirements, through supporting the BDD process, its role has grown to provide guidance in the software design and architecture process as well, with pleasing results.