Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How Testing Can Help You Write Better JavaScript

vjwilson
September 20, 2018

How Testing Can Help You Write Better JavaScript

From a talk given at the CharlotteJS meetup, "How Testing Can Help You Write Better JavaScript" (https://www.meetup.com/CharlotteJS/events/254410581/), on Sept. 20, 2018.

vjwilson

September 20, 2018
Tweet

More Decks by vjwilson

Other Decks in Programming

Transcript

  1. Things that Increase Code Quality Hiring competent employees Providing employee

    training Empowering employees to make decisions Code reviews Continuous Integration (short feedback loops) Test-Driven Development
  2. Definition of Test-Driven Development Write a test for the next

    bit of functionality you want to add. Write the functional code until the test passes. Refactor both new and old code to make it well structured. - Martin Fowler, “TestDrivenDevelopment”, 
 https://martinfowler.com/bliki/TestDrivenDevelopment.html
  3. 1. Write Tests 2. Run Test (TEST FAILS) 3. Write

    Code to Make the Test to Pass 4. Run Test (TEST PASS) 5. Refactor 
 (MAKE CODE CLEANER AND FASTER) 6. Repeat FAIL PASS REFACTOR
  4. Benefits of TDD 1. Writing tests first really helps you

    avoid a lot of bad designs. 
 It makes you think about the code you need to write, BEFORE you write it. 2. Once you have tests, they help you avoid introducing subtle bugs when you have to change the code later. 
 Existing tests help prevent regressions in your code, where adding or changing one thing accidentally breaks another thing.
  5. But if these two things are true, why doesn’t every

    developer write tests first, all the time?
  6. Exercising Gives you more energy and other immediate health benefits.

    Helps you live longer, and with better quality of life, down the road.
  7. Saving money Gives you a better credit rating, and an

    emergency reserve, in the short term. Allows you to retire earlier, and do more things in retirement, in the future.
  8. Let’s assume writing tests first has those benefits… … how

    do you get started, and how do you keep going?
  9. If you’ve looked at testing before… Most material about developers

    writing tests falls into one of two categories. “Here’s how to test a function that add two numbers. Now you can test anything. Good luck.” “You really should not be mocking your flux capacitor, but instead link the flibitz to the your cross-channel bit splicer.”
  10. 1. Management mandates Test-Driven Development 2. Developers TDD all the

    new things (YEAH! TESTS!) 3. Velocity slows because TDD does take more time up-front 4. “We’re not going to meet this deadline ?!?” 
 (STOP DOING TDD) 5. As tests age, skip them or ignore failing tests
 (PEOPLE IGNORE TESTS) Start TDD Deadlines ?!? Graveyard of
 Tests
  11. Can’t we just tests everything with functional tests? With end-to-end

    tests, you have to wait: first for the entire product to be built, then for it to be deployed, and finally for all end-to-end tests to run. When the tests do run, flaky tests tend to be a fact of life. And even if a test finds a bug, that bug could be anywhere in the product. Although end-to-end tests do a better job of simulating real user scenarios, this advantage quickly becomes outweighed by all the disadvantages of the end-to-end feedback loop. — https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html Unit vs. Functional Tests
  12. Anatomy of Any Test Given - setup the environment When

    - act on the System Under Test (SUT) in that environment Then - test what happened
  13. 3 Parts of Every Test Arrange
 “Given a number 10,

    and another number 2” Act
 “When I raise the first number to the power of the second number, 102 Assert
 “I get 100 as an answer”
  14. Testing a pure function it('should return the time remaining', function()

    { // arrange // set some variable // act // call some function // assert // }); Given a certain value, it always returns the same value
  15. Demonstration Given a current time and an end time, return

    a string with the remaining minutes and seconds. 1. Both times will be JavaScript Date object. 2. The current time will never be more than 1 hour before the end time, but may be the same or after. 3. The remaining time should always have 2 digits for both minutes and seconds, even if they are less than 10. Let’s TDD a function
  16. Demonstration if (getTimeRemaining( new Date('2018-07-29T03:24:45'), new Date('2018-07-29T03:24:45')) != '00:00') {

    throw new Error('should return no time if end time is the same as the current time'); } Come up with a first test
  17. Demonstration if (getTimeRemaining( new Date('2018-07-29T03:24:45'), new Date('2018-07-29T03:24:12')) != '00:33') {

    throw new Error('should returning the time difference when subtracting the second arg from the first'); } Add a second test
  18. Demonstration function getTimeRemaining(stopTime, currentTime) { const stopTimestamp = stopTime.getTime(); const

    currentTimestamp = currentTime.getTime(); const totalSecondsRemaining = ((stopTimestamp - currentTimestamp) / 1000); return `00:${totalSecondsRemaining}`; } Make the second test pass
  19. Demonstration throw new Error('should return no time if end time

    is the same as the current time'); ^ Error: should return no time if end time is the same as the current time at Object.<anonymous> (/Users/vwilson/Projects/ personal/javascript/countdown/test.js:2:9) Oooooop! Now the first test breaks! It returns 00:0
  20. Demonstration function getTimeRemaining(stopTime, currentTime) { const stopTimestamp = stopTime.getTime(); const

    currentTimestamp = currentTime.getTime(); const totalSecondsRemaining = ((stopTimestamp - currentTimestamp) / 1000); const formattedSeconds = (`0${totalSecondsRemaining}`).slice(-2); return `00:${formattedSeconds}`; } Make both tests pass
  21. Demonstration if (getTimeRemaining( new Date('2018-07-29T03:24:45'), new Date('2018-07-29T03:04:36')) != '20:09') {

    throw new Error('should returning a time difference that includes minutes and seconds'); } Add a third test
  22. Demonstration function getTimeRemaining(stopTime, currentTime) { const stopTimestamp = stopTime.getTime(); const

    currentTimestamp = currentTime.getTime(); const totalSecondsRemaining = ((stopTimestamp - currentTimestamp) / 1000); const minutesRemaining = Math.floor(totalSecondsRemaining / 60); const formattedSeconds = (`0${totalSecondsRemaining}`).slice(-2); return `${minutesRemaining}:${formattedSeconds}`; } Make all three tests pass
  23. Demonstration if (getTimeRemaining( new Date('2018-07-29T03:24:45'), new Date('2018-07-29T03:22:46')) != '01:59') {

    throw new Error('should returning a time difference where minutes less than 10'); } Add a fourth test
  24. Demonstration function getTimeRemaining(stopTime, currentTime) { … const minutesRemaining = Math.floor(totalSecondsRemaining

    / 60); const secondsRemaining = totalSecondsRemaining % 60; const formattedMinutes = (`0${minutesRemaining}`).slice(-2); const formattedSeconds = (`0${secondsRemaining}`).slice(-2); return `${formattedMinutes}:${formattedSeconds}`; } Make all four tests pass