Automated Testing in JS
by Michael Yagudaev
About Michael
• Co-Founder @ Nano 3 Labs & PictureThat

• Full-stack Developer Rails/React

• Writing tests since 2010

• Web dev since 2001

• Love UX & apps that just work

• Early co-founder of Winnipeg.js
Outline
• Goal: get you started using automated testing
• Why Test?
• How to think of tests?
• Testing tools in Javascript
• Example
• Common Mistakes
• Tips
Why Write Tests?
Because it’s 2018!
But really…
• Documents functionality
• Proves your code works
• Forces you to think things through
• Gives confidence in new changes
• Shorter feedback loop for devs
• More frequent releases
How Testing Saved Me
• Artona - Photography E-Commerce
• 2500 tests for mid-size e-comm app + 300 selenium tests
• 8 year old code base
• Rails Upgraded from 2.3 => 5.2 over last 4 years
• Rewrite of Admin, Desktop from Flash to React
• Launched Mobile Web App rewrite in June without any
major issues
Testing tools in JS
• Many test frameworks over the years… most are hard to
configure, brittle and have bad documentation
• But there is hope:
• Jest - simple testing framework for unit-testings and
functional testing
• Cypress - innovative end-to-end web testing
• Detox - react-native testing*
* needs lots of work
At what level should I test?
• Generally: Highest abstraction to lowest abstraction
possible
• Start with UI Acceptance Tests (aka End-to-End tests)
• Focus on User Stories and act like user
• Move down to lower tests for more complicated tests
• Integration Tests => Functional Tests => Unit Tests
What is a test case?
• A test case is an Assumption or Hypothesis we wish
to verify is true
• There are 4 parts to any test:
• 1) Setup
• 2) Execute code under test
• 3) Matching expectations => Assertions
• 4) Cleanup (Optional)
Given-When-Then
• A test can be expressed as follow
• Given I am on the login page
• When I enter my email and password
• And click Login
• Then I should see “Welcome back, Michael”
• Known as Cucumber syntax — it provides us with a
useful way of thinking
Demo Cypress
• E2E Testing
Why Cypress?
• No timeouts
• Deterministic
• Developer exp - pause, time travel, auto-reload, etc
• Great documentation
• Event based - no crazy async logic — think Redux for
testing
When to use “lower-level”
tests?
• Can’t easily write an integration test
• Specific edge cases
• Too much setup for integration test
• Confident in design choice
• e.g. unit test checking user without email address
cannot be saved to DB
Common Mistake
• Making test steps too specific => hard to understand
purpose of test
•
it('A User logs in and sees a welcome message', () => {
cy.visit('http://localhost:3000')
cy.get('[name="email"]').type(email)
cy.get('[name="password"]').type(password)
cy.get('button').click()
expect(cy.contains('Successfully submitted')).toBeTruthy
})
// better
it('A User logs in and sees a welcome message', () => {
loginWith('michael@nano3labs.com', 'passsword')
expect(cy.contains('Successfully submitted')).toBeTruthy
})
Common Mistakes
• Adding details not under test => unnecessary noise
•
it('A User fills in a signup form, but forgets to enter a password and
sees error message', () => {
cy.get('[name="fullName"').type('Bob Smith')
cy.get('[name="email"]').type('bob@gmail.com')
cy.get('[name="password"]').type('')
cy.get('button').click()
})
// better
it('A User fills in a signup form, but forgets to enter a password and
sees error message', () => {
cy.get('[name="password"]').type('')
cy.get('button').click()
})
Common Mistakes
• Testing 3rd party libraries
• e.g. S3 => just mock out and move on, expect
function to be called
• Complicating implementation to make things more
testable… You should add minimal if any production
code to support tests
• e.g. making private members public
Tips
• Setup a Continuous Integration Env (CircleCI, Travis,
etc)
• Setup Code Coverage Visualization
• Setup ESLint as a step in your CI build
Terms: Cheat Sheet
• TDD - Test Driven Development
• BDD - Behaviour Driven Development
• Matcher - expressing matching criteria for an assertion
• Expectation - func we wrap around test result to setup an assertion using a matcher
• Double - a fake implementation of an object (instance)
• Spy - like a mock, but
• Mock - a fake implementation of a function => allow to test in isolation
• Stub - same as mock
• Fixture - use static objects in a separate file for testing
• Factory - uses Factory pattern to generate objects for testing
Questions?
• Share your experience or ask a question…
• twitter: @yagudaev, @nano3labs
• P.S. If you want to come to Vancouver…. 

We’re Hiring… :)

Automated testing in javascript

  • 1.
    Automated Testing inJS by Michael Yagudaev
  • 2.
    About Michael • Co-Founder@ Nano 3 Labs & PictureThat • Full-stack Developer Rails/React • Writing tests since 2010 • Web dev since 2001 • Love UX & apps that just work • Early co-founder of Winnipeg.js
  • 3.
    Outline • Goal: getyou started using automated testing • Why Test? • How to think of tests? • Testing tools in Javascript • Example • Common Mistakes • Tips
  • 4.
  • 5.
    But really… • Documentsfunctionality • Proves your code works • Forces you to think things through • Gives confidence in new changes • Shorter feedback loop for devs • More frequent releases
  • 6.
    How Testing SavedMe • Artona - Photography E-Commerce • 2500 tests for mid-size e-comm app + 300 selenium tests • 8 year old code base • Rails Upgraded from 2.3 => 5.2 over last 4 years • Rewrite of Admin, Desktop from Flash to React • Launched Mobile Web App rewrite in June without any major issues
  • 7.
    Testing tools inJS • Many test frameworks over the years… most are hard to configure, brittle and have bad documentation • But there is hope: • Jest - simple testing framework for unit-testings and functional testing • Cypress - innovative end-to-end web testing • Detox - react-native testing* * needs lots of work
  • 8.
    At what levelshould I test? • Generally: Highest abstraction to lowest abstraction possible • Start with UI Acceptance Tests (aka End-to-End tests) • Focus on User Stories and act like user • Move down to lower tests for more complicated tests • Integration Tests => Functional Tests => Unit Tests
  • 9.
    What is atest case? • A test case is an Assumption or Hypothesis we wish to verify is true • There are 4 parts to any test: • 1) Setup • 2) Execute code under test • 3) Matching expectations => Assertions • 4) Cleanup (Optional)
  • 10.
    Given-When-Then • A testcan be expressed as follow • Given I am on the login page • When I enter my email and password • And click Login • Then I should see “Welcome back, Michael” • Known as Cucumber syntax — it provides us with a useful way of thinking
  • 11.
  • 12.
    Why Cypress? • Notimeouts • Deterministic • Developer exp - pause, time travel, auto-reload, etc • Great documentation • Event based - no crazy async logic — think Redux for testing
  • 13.
    When to use“lower-level” tests? • Can’t easily write an integration test • Specific edge cases • Too much setup for integration test • Confident in design choice • e.g. unit test checking user without email address cannot be saved to DB
  • 14.
    Common Mistake • Makingtest steps too specific => hard to understand purpose of test • it('A User logs in and sees a welcome message', () => { cy.visit('http://localhost:3000') cy.get('[name="email"]').type(email) cy.get('[name="password"]').type(password) cy.get('button').click() expect(cy.contains('Successfully submitted')).toBeTruthy }) // better it('A User logs in and sees a welcome message', () => { loginWith('[email protected]', 'passsword') expect(cy.contains('Successfully submitted')).toBeTruthy })
  • 15.
    Common Mistakes • Addingdetails not under test => unnecessary noise • it('A User fills in a signup form, but forgets to enter a password and sees error message', () => { cy.get('[name="fullName"').type('Bob Smith') cy.get('[name="email"]').type('[email protected]') cy.get('[name="password"]').type('') cy.get('button').click() }) // better it('A User fills in a signup form, but forgets to enter a password and sees error message', () => { cy.get('[name="password"]').type('') cy.get('button').click() })
  • 16.
    Common Mistakes • Testing3rd party libraries • e.g. S3 => just mock out and move on, expect function to be called • Complicating implementation to make things more testable… You should add minimal if any production code to support tests • e.g. making private members public
  • 17.
    Tips • Setup aContinuous Integration Env (CircleCI, Travis, etc) • Setup Code Coverage Visualization • Setup ESLint as a step in your CI build
  • 18.
    Terms: Cheat Sheet •TDD - Test Driven Development • BDD - Behaviour Driven Development • Matcher - expressing matching criteria for an assertion • Expectation - func we wrap around test result to setup an assertion using a matcher • Double - a fake implementation of an object (instance) • Spy - like a mock, but • Mock - a fake implementation of a function => allow to test in isolation • Stub - same as mock • Fixture - use static objects in a separate file for testing • Factory - uses Factory pattern to generate objects for testing
  • 19.
    Questions? • Share yourexperience or ask a question… • twitter: @yagudaev, @nano3labs • P.S. If you want to come to Vancouver…. 
 We’re Hiring… :)