Testing with Cypress
and Jest
Introduction
Types of Tests + Frameworks
Webdriver vs. Cypress
- Webdriver
- Remote-control browser
- Works over network
- W3c recommendation
- Works with all major browsers
- Widely used (incl. large component vendors π)
- Cypress
Cypress Limitations π
What's unique about Cypress?
- Fine control over browser because Cypress runs insideΒ the browser
- Snapshots after each command
- Relatively easy to diagnose problems
- No need for writing async code manually
- Cypress specs build test commands
- Cypress commands are queued and
asynchronously executed at a later time π - Async behavior (e.g. waiting) is built-in
Get Started!
Cypress CLI π
Command | Description |
---|---|
cypress open | Opens the Cypress app |
cypress run | Runs Cypress tests (headlessly) |
cypress info | Prints information about Cypress environment |
cypress verify | Checks Cypress installation |
cypress version | Prints version info |
Demo
Time!
Exercise: First E2E Test
# Create new folder, navigate to it
npm init
npm install -D cypress
# Add npm script for `cypress open` and `cypress run`
# Start cypress
# Select E2e -> configures your cy environment
# Generate new, empty spec for Chrome
describe('My First Test', () => {
it('Does not do much!', () => {
expect(true).to.equal(true)
})
})
# Run test
# Change test so that it fails
# Rerun test
Exercise: First E2E Test
describe('My First Test', () => {
it('Gets, types and asserts', () => {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
// Should be on a new URL which
// includes '/commands/actions'
cy.url().should('include', '/commands/actions')
// Get an input, type into it and verify
// that the value has been updated
cy.get('#email1')
.type('fake@email.com')
.should('have.value', 'fake@email.com')
})
})
# Run test
# Change test so that it fails
# Rerun test
Exercise: First E2E Test
# Install typescript
npm install -D typescript
# Add tsconfig.json in `cypress` folder
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts"]
}
# Rename spec.cy.js to .ts
# Reopen cypress to see TypeScript test running
Selection, Naviation
$
Hello jQuery
<!-- HTML -->
<button id="main" class="btn btn-large" name="submission"
role="button" data-testid="submit">
Submit
</button>
// Cypress
cy.get('[data-testid="submit"]').click();
Asynchronous Operations
- Cypress wraps all DOM queries with retry-and-timeout logic
- β οΈ Make sure your testing code is idempotent
- No error if e.g. ...
- ...DOM not yet loaded
- ...XHR not finished
- ...framework bootstrapping in progress
- Customize timeouts if necessary π
- Option: cy.get('...', { timeout: 10000 })
- Get DOM element once it becomes available: .thenΒ π
- Note Cypress.$ has to be executed in .then π
Demo
Time!
Exercise: Querying
- Discuss: https://example.cypress.io/commands/querying
- Use Stackblitz πΒ to create a new Angular app
<!-- app.component.html -->
<hello name="{{ name }}"></hello>
<p>Start editing to see some magic happen :)</p>
<button data-test-cy="main-button">Do something</button>
it('Querying', () => {
cy.visit('https://angular-ivy-saxmjf.stackblitz.io')
cy.get('#promptToRun', { timeout: 600000 }).find('button').click()
cy.get('[data-test-cy="main-button"]')
.should('have.text', 'Do something')
})
Interactions
Interacting with elements
Assertations
Demo
Time!
Exercise: TS, Cmds, Interactions
- Delete/remove support/commands.js
- Create folder support/commands
- Create index.d.ts in support folder
export {};
declare global {
namespace Cypress {
interface Chainable {
skipStackblitzProtection(): Chainable<Element>;
}
}
}
Exercise: TS, Cmds, Interactions
- Create skipStackblitzProtection.tsΒ in support/commands
Cypress.Commands.add('skipStackblitzProtection', () => {
cy.get('#promptToRun', { timeout: 600000 }).find('button').click();
});
- Change support/e2e.js
import "./commands/skipStackblitzProtection";
Exercise: TS, Cmds, Interactions
it('Querying', () => {
cy.visit('https://angular-ivy-saxmjf.stackblitz.io')
cy.skipStackblitzProtection()
cy.get('[data-test-cy="main-button"]')
.should('have.text', 'Do something')
})
it('Calculates correctly', () => {
cy.visit('https://angular-basic-calculator.stackblitz.io/')
cy.get('.button-1').as('1').click()
cy.get('.button-0').click()
cy.get('.button-a').click()
cy.get('@1').click()
cy.get('.equals').click()
cy.get('.display').should('have.text', 10 + 1)
})
Test Organization
Folders π
Folder | Description |
---|---|
e2e | Test files |
fixtures | Static data used by tests |
support | Support files running before each spec |
Organize Tests
- Folder for each page
- E.g. orders, settings, account
- Inside, spec for each test use case
- E.g. account/register_spec.js
- Test structure π
- `shared` folder for shared component tests
- E.g. header bar
Troubleshooting Tests
Troubleshooting Tests π
- Use the debugger, it just works
- Check the browser's console window
- Also when clicking on commands like .type()
- Use .debug()Β to trigger breakpoint and get detailed information about a yielded object
- Β
Server
Dealing with Server
Stubbing Requests
Jest
Jest
- Modern, fast, widely used testing framework for JS/TS
- Use jest-preset-angular for Jest + Angularπ
- Simple Jasmine tests work without any changes
- itΒ is alias for Jest's testΒ π
- Pros π
- Fine testing framework with great API
- No need for Karma
- Well documented
- Widely used
- Cons π
- Doesn't come out of the box in Angular
- Requires extra dependencies
Demo
Time!
Jest in Angular
- Remove Jasmine and Karma from package.json
- Remove karma.conf.js and src/test.ts
- Remove testΒ area from angular.json
- Install Jest π
- npm install --save-dev jest jest-preset-angular @types/jest
- Configure jest-preset-angular as shown in π
- Set "esModuleInterop": trueΒ in tsconfig.json
- Replace ng testΒ with jestΒ in package.json script
- Good summary of whole process in π
Expects and Matchers π
Setup and Teardown π
- Use before/afterEachΒ for setup and teardown per test
- Use before/afterAllΒ for one-time setup and teardown per file
- Group tests using describe
- βSetup/teardown methods are scoped in test group
- Use beforeEachΒ to configure TestBed in Angular π
Mocking π
Further topics
- Snapshot testing π
- Renders HTML or UI component
- Compares against baseline
- Problem: Changes in rendered HTML (particularly 3rd party controls)
- Alternative: Angular DOM testing, e2e testing
- Timer mocks π
- Replace JS's timeout/interval functions
- Control timers programmatically to speed up long-running tests
- Reference: Fake Timers π
cypress-testing
By Rainer Stropek
cypress-testing
- 535