Note: the example code for this post can be found here. See the repo’s README for instructions on how to install & run the tests.
Test Driven Development (TDD) is often encouraged in agile development. It’s useful for defining unit tests from requirements before writing functional code.
Behavior Driven Development (BDD) focuses on features of an application from an end user’s perspective. This is different than Test Driven Development which focuses more on smaller components, often from a developer’s perspective. BDD features are written using text (not code) which can then involve all members of the team — business users, developers, and QA.
The value of this is that the functionality of an application can be defined by a number of text-based features and example scenarios. These scenarios can then be used to test and verify the application.
In this way:
- Business users (product owners, stakeholders, business analysts) can help define all core features and clarifying scenarios of the application
- Developers can implement the functionality defined by the feature definitions
- QA can run the features and scenarios as actual tests against the implementation and confirm that the tests pass.
This is a good way to involve all team members in the development process.
The development and QA teams will still want to create many other tests that business users may not care as much about. These tests may include items like bug fixes, edge cases, boundary conditions, etc.
Tools that support both BDD tests that the entire team can use (i.e. text-based tests) as well as tests that the development and QA teams can use (i.e. code-based tests) are a good combination for a team to have.
BDD in Test Automation
CodeceptJS is an end-to-end testing framework that supports two different types of BDD tests:
- Gherkin based common-language (text-based) Features and Scenarios. This is useful for all team members (business users, developers, and QA).
- Code-based (Node.js) Features and Scenario tests. This is useful for additional development and QA tests.
CodeceptJS can be configured to run with many different test automation frameworks such as Playwright, Puppeteer, TestCafe, and WebDriverIO. It also supports other mobile testing and API testing.
Example: Ecommerce test website
Let’s say that we have an ecommerce website and we want to write some features and scenarios definitions for the functionality we would like to implement and test.
For the purpose of writing features and scenarios, let’s use this fake, test ecommerce website: automationpractice.com.

Here are some common pages and functionality that you usually find on an ecommerce website:
- Pages: Home page, Product List Page (PLP) or Category page, Product Detail Page (PDP), Cart page, Checkout page, Confirmation page, etc.
- Functionality: Search, Login/Logout, Email signup, etc.
CodeceptJS test types
Using BDD, you would write separate features and scenarios for each page or area of functionality. With CodeceptJS, a BDD feature of the Category page could be defined (using only text, no code) like this:
# file: category.feature Feature: Category In order to purchase a product As a user on the Category page I want to be able to add a product to the Cart Scenario: can add product to cart Given I am on the home page And I search for "dress" And I add product "random" to cart Then I should see 1 product in cart
This feature describes the ability on a Category page to add a product to the cart. The scenario gives a concrete example of steps that could be taken and the result you would expect see. Scenarios are especially useful in helping provide examples in cases where features are more abstract.
In CodeceptJS, here is the same Category page feature, only this time defined using Node.js javascript code:
// file: category.test.js const assert = require('node:assert'); Feature('Category'); Scenario(`can add product to cart`, async ({ I, search, category, cart }) => { I.amOnPage('/'); await search.search('dress'); await category.addProductToCart('random'); const expectedQuantity = 1; const cartQuantity = await cart.getQuantity(); assert.equal( cartQuantity, expectedQuantity, `Expected ${expectedQuantity}, but found ${cartQuantity} products in Cart` ); });
The feature and scenario are defined with similar text as before. Additional objects are passed to the Scenario function:
- “I” object – this is the browser actor object. You can make direct calls to the “I” object to click on elements, fill fields, grab element values, assert expected results, wait for elements, etc.
- “search”, “category”, “cart” objects are page objects that aggregate “I” object calls into simpler common functions. These make it easier to write object-oriented, more readable code.
The rest of the function performs similar calls and assertions to verify the expected results.
How the text-based test works
Note that in CodeceptJS, both of these are tests that can be run against our test website. In the first, text-only test, when the test is run, each line of the test is parsed and compared against matching defined function calls. The parser uses Cucumber Expressions which are described as “an alternative to Regular Expressions with a more intuitive syntax”.
Here is an excerpt of the file of functions that CodeceptJS uses when looking for matching lines in the text-based feature file:
// file: steps.js // Search Given('I search for {string}', async (term) => { await search.search(term); }); // Category Given('I add product {string} to cart', async (choice) => { await category.addProductToCart(choice); }); Given('I should see products returned', async () => { const productCount = await category.productCount(); assert.notEqual( productCount, 0, `found ${productCount} products after search` ); }); // Cart Given('I should see {int} product(s) in cart', async (quantity) => { const cartQuantity = await cart.getQuantity(); assert.equal( cartQuantity, quantity, `Expected ${quantity}, but found ${cartQuantity} product(s) in Cart` ); });
So you can see, if a line of the feature file matches one of the Given()
function definitions in this steps.js
file, then that function gets called (along with any found parameters) at that step in the test.
Running the tests
When the test is run, CodeceptJS will use the automation framework configured in the project (Playwright in this case), launch a browser, and navigate through the test steps.
Here is what the test output of the text-based Category test looks like when it is run (notice the “–features” command-line argument telling it to run “*.feature” tests):

Similarly, here is what the output of the code-based Category test looks like when it is run (notice the “–tests” command-line argument telling it to run “*.test.js” tests):

Conclusions
In this way, CodeceptJS can be used to support two different types of Behavior Driven Development tests:
- Text-based tests:
- easily readable & writeable by all team members (i.e. greater involvement by all, not just developers)
- best for defining & documenting a smaller set of core functionality & tests
- Code-based tests:
- good for developers & QA
- useful for additional, needed tests such as:
- bug fix tests
- negative tests
- edge case tests
- good for defining a larger body of additional tests
CodeceptJS has many other features and abilities (that can be seen at their website), but its ability to define tests from the user’s perspective (BDD) and its flexibility of supporting different types of tests for different team members make it an interesting test automation tool to explore further.
Feel free to download the repository from the link at the top of the page to try out more features and tests.