I remember quite vividly the moment I got started with testing. It was the winter of 2012 and I was freelancing for the company Zilverline.
Before this day in December, I tested how most people tested back then (and maybe still today)—manually, by repeating scenarios. So while I was working on a project I was just clicking through the website and seeing if anything was broken.
While this is a great way to test software, there are other ways that give you more certainty that everything is working.
The first question you should ask is why would I write an automated test? For me, the easiest answer is one word: “confidence.” Without tests, I cannot have confidence that my software works as designed. When you start adding automated tests you gain confidence that the software works as expected. This allows you to deploy automatically after each change (also known as “continuous deployment”).
Tests can also be viewed as a way to record a certain behavior of the software. I will illustrate this with a real-world example.
In this example, I want to test that the website knows how to handle a certain state.
For this example, I’m using Ruby RSpec because of its readability.
it "shows a message when there are no invites available" do sign_in create(:user) visit invites_path expect(page).to have_content "No invites available" end
As you can see, I’m only testing one thing at a time. I’ll try to make the test as narrow as possible so it won’t break when there is something wrong on the other side of the application.
There are four phases in a test as you see in the example above. These phases could have a new line between them for readability.
In the example above, you saw the testing language RSpec, which has a readable syntax. However, if you want to take it one step further in terms of readability, then you could go for the Gherkin language.
Below you see a simple example of a test:
Feature: Sign in I want to see the sign in page Scenario: Shows an error with wrong password Given I visit the homepage When I sign in with wrong password Then I should see that the credentials are wrong
It even has more advanced features like
Scenario Outline: eating Given there are <start> cucumbers When I eat <eat> cucumbers Then I should have <left> cucumbers Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 |
The current fanatical TDD experience leads to a primary focus on the unit tests, because those are the tests capable of driving the code design (the original justification for test-first).
I don’t think that’s healthy. Test-first units leads to an overly complex web of intermediary objects and indirection in order to avoid doing anything that’s “slow”. Like hitting the database. Or file IO. Or going through the browser to test the whole system. It’s given birth to some truly horrendous monstrosities of architecture. A dense jungle of service objects, command patterns, and worse.
When I started testing, I also first focussed on the tests. And then I started implementing the code. After seeing David’s talk at Rails Conf, I realized that testing first isn’t always the best way to write code.
I started to look at ways to do full-stack front-end testing and about a year ago I ran into Cypress.
With Cypress, you can control a browser that interacts with your project. It provides a really powerful API so everything you can do in a browser is available.
In the project for Close, I’m using Gherkin to write tests against Chrome. After you run the test, you get a video of the test that looks like this: