How I'm using Behat to test my personal website

This website is rather simple and easy to use. That means that there isn't much to test, but having some sort of automated test coverage is giving me peace of mind that everything works as it should.

I'm primarily a Drupal developer, so it's kind of obvious that this website is built in Drupal 8 (soon to be upgraded to version 9). Behat is an open-source behavior-driven testing framework for PHP, and it has great integration with Drupal. Also, writing Behat tests is fun and easy, so no wonder I decided to use it to create automated tests.

I don't have any fancy features on the admin side, so all I need to test are user-facing features. That means making sure that my header, footer, menus, links and so on exist and are placed according to the design.

I have already written about setting up and running Behat tests, so if you want to find out more check out my Drupal 8 and Behat tests article. If you want to add Behat tests to your website, you should read the article because you will find out what you need to do to run the tests.

Tests for my website are executed automatically every Sunday at 4 AM. I'm using Gitlab for CI/CD, so I added a scheduled job to run the tests.

Gitlab CI/CD scheduled job and Behat tests
Gitlab CI/CD scheduled job and Behat tests

I added a job to the Gitlab CI config file that is executed only when the pipeline has been scheduled. The test job looks like this:

prod:behat:
  stage: test
  allow_failure: false
  script:
    - "cd /var/www/gorannikolovski.com; bin/behat"
  only:
    - schedules

As you can see the job is pretty simple -- all it does is changing to the correct directory and then executing bin/behat command that will run the tests.

Because of the nature of what I'm testing, the tests are executed on the production server. I could also run tests after each deploy, but I don't deploy that often so having scheduled testing is a better option.

And finally, here are the tests:

@api
Feature: Test website

Background:
  Given I am not logged in

Scenario: Header region
  Given I am on "/"
  Then I should see the raw text matching "/themes/custom/gn2021/images/goran-nikolovski-signature.png" in the "header" region
  And I should see "Home" in the "header" region
  And I should see "Snippets" in the "header" region
  And I should see "Search" in the "header" region
  And I should see "Contact" in the "header" region
  And I should see "About" in the "header" region
  And I should see "Hi, I'm Goran Nikolovski. I'm a creator, speaker, open-source contributor, web developer specialized in Drupal and DevOps enthusiast. I'm based in Subotica, Serbia." in the "header" region

Scenario: Content region
  Given I am on "/"
  Then I should see the link "Read more"
  And I should see "Next ›" in the "content" region
  And I should see "Last »" in the "content" region

Scenario: Featured bottom region
  Given I am on "/"
  Then I should see "Get new posts delivered right to your inbox." in the "featured_bottom" region
  And I should see the raw text matching "Your email address ..." in the "featured_bottom" region
  And I should see the raw text matching "Subscribe" in the "featured_bottom" region

Scenario: Footer region
  Given I am on "/"
  Then I should see "Copyright © 2017-2020 by Goran Nikolovski. All rights reserved. Top of page." in the "footer" region

Scenario: Social bar region
  Given I am on "/"
  Then I should see the raw text matching "https://gorannikolovski.com/rss.xml" in the "social_bar" region
  And I should see the raw text matching "https://github.com/gnikolovski" in the "social_bar" region
  And I should see the raw text matching "https://www.drupal.org/u/gnikolovski" in the "social_bar" region
  And I should see the raw text matching "https://www.linkedin.com/in/goran-nikolovski" in the "social_bar" region
  And I should see the raw text matching "https://twitter.com/_gnikolovski" in the "social_bar" region

Scenario: Response status for pages
  Given I am on "/"
  Then the response status code should be 200
  Given I am on "/snippets"
  Then the response status code should be 200
  Given I am on "/search"
  Then the response status code should be 200
  Given I am on "/contact"
  Then the response status code should be 200
  Given I am on "/about"
  Then the response status code should be 200
  Given I am on "/privacy-policy"
  Then the response status code should be 200
  Given I am on "/terms-of-service"
  Then the response status code should be 200
  Given I am on "/rss.xml"
  Then the response status code should be 200

Scenario: Snippets page
  Given I am on "/snippets"
  Then I should see "Snippets"
  And I should see "Next ›" in the "content" region
  And I should see "Last »" in the "content" region

Scenario: Search page
  Given I am on "/search"
  Then I should see "Search"
  And I should see "Displaying 0 - 0 of 0"
  And I should see "No results match your search criteria."
  And I should see the raw text matching "Search" in the "content" region

Scenario: Contact page
  Given I am on "/contact"
  Then I should see "Contact"
  And I should see "Your Name"
  And I should see "Your Email"
  And I should see "Subject"
  And I should see "Message"
  And I should see the raw text matching "Send message" in the "content" region

Scenario: About page
  Given I am on "/about"
  Then I should see "About me"
  And I should see "My Visits"
  And I should see "Qualifications"
  And I should see "Projects"

Scenario: Log in page
  Given I am on "/account/login"
  Then I should see "Log in"
  And I should see the link "Log in"
  And I should see the link "Reset your password"
  And I should see "Username"
  And I should see "Password"
  And I should see the raw text matching "Log in" in the "content" region

Scenario: Response status for articles
  Given I am on "/blog/couchbase-api-rate-limiter"
  Then the response status code should be 200
  Given I am on "/blog/hierarchical-taxonomy-menu"
  Then the response status code should be 200
  Given I am on "/blog/drupal-8-installing-composer"
  Then the response status code should be 200
  Given I am on "/blog/dix-database-import-export"
  Then the response status code should be 200
  Given I am on "/blog/react-js-and-drupal-8-building-decoupled-website-part-1"
  Then the response status code should be 200
  Given I am on "/blog/2017-recap"
  Then the response status code should be 200
  Given I am on "/blog/amazing-facts-about-drupal"
  Then the response status code should be 200
  Given I am on "/blog/drupal-camp-pannonia-2018"
  Then the response status code should be 200
  Given I am on "/blog/programmatically-create-menu-link-node"
  Then the response status code should be 200
  Given I am on "/blog/drupal-8-nginx-and-lets-encrypt"
  Then the response status code should be 200
  Given I am on "/blog/4-ways-delete-configuration-items-drupal-8"
  Then the response status code should be 200
  Given I am on "/blog/drupal-8-and-behat-tests"
  Then the response status code should be 200
  Given I am on "/blog/inline-twig-templates"
  Then the response status code should be 200
  Given I am on "/blog/2018-recap"
  Then the response status code should be 200
  Given I am on "/blog/alternative-menu-token-module"
  Then the response status code should be 200
  Given I am on "/blog/set-date-field-programmatically"
  Then the response status code should be 200
  Given I am on "/blog/how-loop-through-referenced-entities"
  Then the response status code should be 200
  Given I am on "/blog/out-stock-feature-drupal-commerce-2x"
  Then the response status code should be 200
  Given I am on "/blog/example-usage-twig-render-this"
  Then the response status code should be 200
  Given I am on "/blog/block-caching-examples"
  Then the response status code should be 200
  Given I am on "/blog/docker4drupal-and-functional-javascript-tests"
  Then the response status code should be 200
  Given I am on "/blog/easy-way-speed-your-website"
  Then the response status code should be 200
  Given I am on "/blog/taxonomy-post-update-make-taxonomy-term-revisionable-fails"
  Then the response status code should be 200
  Given I am on "/blog/add-computed-field-jsonapi-response"
  Then the response status code should be 200
  Given I am on "/blog/undefined-index-name-system-requirements"
  Then the response status code should be 200
  Given I am on "/blog/how-add-column-taxonomy-overview-page"
  Then the response status code should be 200
  Given I am on "/blog/menu-links-dynamic-values"
  Then the response status code should be 200
  Given I am on "/blog/drupalcamp-pannonia-2019"
  Then the response status code should be 200
  Given I am on "/blog/programmatically-update-search-api-index"
  Then the response status code should be 200
  Given I am on "/blog/batch-processing-and-update-hooks-drupal-8"
  Then the response status code should be 200
  Given I am on "/blog/how-alter-inline-entity-form-table-fields"
  Then the response status code should be 200
  Given I am on "/blog/query-level-filtering-custom-entities-drupal-8"
  Then the response status code should be 200
  Given I am on "/blog/svg-formatter-v110"
  Then the response status code should be 200
  Given I am on "/blog/drupal-9-readiness-checklist"
  Then the response status code should be 200
  Given I am on "/blog/making-my-modules-drupal-9-ready"
  Then the response status code should be 200
  Given I am on "/blog/drupal-9-and-drush-10"
  Then the response status code should be 200
  Given I am on "/blog/how-set-menu-link-attribute-programmatically"
  Then the response status code should be 200
  Given I am on "/blog/2019-recap"
  Then the response status code should be 200
  Given I am on "/blog/drupal-commerce-and-facebook-product-catalog"
  Then the response status code should be 200
  Given I am on "/blog/simplify-drupal-commerce-2x-checkout-removing-login-or-continue-guest-pane"
  Then the response status code should be 200
  Given I am on "/blog/browsersync-gulp-docker4drupal-and-my-new-theme"
  Then the response status code should be 200
  Given I am on "/blog/my-new-theme"
  Then the response status code should be 200
  Given I am on "/blog/how-convert-existing-image-fields-media-images"
  Then the response status code should be 200
  Given I am on "/blog/4-cool-new-php-8-features"
  Then the response status code should be 200
  Given I am on "/blog/how-im-using-behat-test-my-personal-website"
  Then the response status code should be 200