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

Tips for improving test assertions at Codurance

Marabesi
November 18, 2021

Tips for improving test assertions at Codurance

Testing with jest is an activity that developers do to keep the application maintainable and time proof. Therefore, learning a testing framework can be a consuming task, often it has many features to master. The assertion API usually is one of the most important ones, as this is the one that the developer uses the most during the TDD flow.

The gist of the assertion API is to compare values, as such the equals matcher is the most used. Being one of the most used can also point to a lack of knowledge in the different assertions that the testing framework offers.

This talk aims to cover different assertions, to avoid using always toEqual and make the test case more expressive. For each example, I try to first depict how it would be with toEqual, then I show another way using a different assertion.

Marabesi

November 18, 2021
Tweet

More Decks by Marabesi

Other Decks in Programming

Transcript

  1. Matheus Marabesi Hello there, you can call me Marabesi, But

    my name is Matheus Marabesi, I work at Codurance as a Software craftsperson. I enjoy talking about anything related to: testing, patterns and gamification. You can find me at @MatheusMarabesi or https://marabesi.com Codurance Crafting Code
  2. 1. Intro - Background 2. Assertion smells 3. Assertion API

    4. Exploring assertions 5. Wrapping up Crafting code Agenda
  3. 1. Intro - Background 2. Assertion smells 3. Assertion API

    - Jest 4. Exploring assertions 5. Wrapping up Crafting code Agenda
  4. TDD

  5. Assert test('assert some value', () => { const myVar =

    'some val' expect(myVar).toEqual('some val') })
  6. Assert test('assert some value', () => { const myVar =

    'some val' expect(myVar).toEqual('some val') ✅ })
  7. Assert test('light is on', () => { const off =

    false expect(off === false).toEqual(true) })
  8. Assert test('light is on', () => { const off =

    false expect(off === false).toEqual(true) ✅ })
  9. Assert test('it has two users', () => { const users

    = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).toEqual(2) })
  10. Assert test('it has two users', () => { const users

    = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).toEqual(2) ✅ })
  11. 2. Assertion smells Smells are something that developers relate to,

    has some insight that it's not the best approach and still can't figure out what. Crafting code
  12. Assertion smells const myVar = 'some val' expect(myVar).equals('some val') ✅

    const off = false expect(off === false).equals(true) ✅ const users = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).equals(2) ✅
  13. Assertion smells const myVar = 'some val' expect(myVar).equals('some val') ✅

    const off = false expect(off === false).equals(true) ✅ const users = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).equals(2) ✅
  14. Assertion smells const myVar = 'some val' expect(myVar).equals('some val') ✅

    const off = false expect(off === false).equals(true) ✅ const users = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).equals(2) ✅
  15. Assertion smells const myVar = 'some val' expect(myVar).equals('some val') ✅

    const off = false expect(off === false).equals(true) ✅ const users = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).equals(2) ✅
  16. Assertion smells const myVar = 'some val' expect(myVar).equals('some val') ✅

    const off = false expect(off === false).equals(true) ✅ const users = [ { name: 'jhon'}, { name: 'maria' } ] expect(users.length).equals(2) ✅
  17. test('it returns a number', () => { const isNumber =

    number => number expect(typeof isNumber(2)).toEqual('number') }) test('it returns a number', () => { const isNumber = number => number expect(isNumber(2)).toEqual(expect.any(Number)) }) Numbers 🧱
  18. test('it returns a number', () => { const isNumber =

    number => number expect(typeof isNumber(2)).toEqual('number') }) test('it returns a number', () => { const isNumber = number => number expect(isNumber(2)).toEqual(expect.any(Number)) }) Numbers 🧱
  19. test('it returns a number', () => { const isNumber =

    number => number expect(typeof isNumber(2)).toEqual('number') }) test('it returns a number', () => { const isNumber = number => number expect(isNumber(2)).toEqual(expect.any(Number)) }) Numbers 🧱
  20. test('it returns a number', () => { const isNumber =

    number => number expect(typeof isNumber(2)).toEqual('number') }) test('it returns a number', () => { const isNumber = number => number expect(isNumber(2)).toEqual(expect.any(Number)) }) Numbers 🧱
  21. test('expected is greater than the desired', () => { const

    expected = 10 const actual = 3 expect(expected > actual).toEqual(true) }) test('expected is greater than the desired', () => { const expected = 10 const actual = 3 expect(expected).toBeGreaterThan(actual) }) Booleans 🧱
  22. test('expected is greater than the desired', () => { const

    expected = 10 const actual = 3 expect(expected > actual).toEqual(true) }) test('expected is greater than the desired', () => { const expected = 10 const actual = 3 expect(expected).toBeGreaterThan(actual) }) Booleans 🧱
  23. test('expected is greater than the desired', () => { const

    expected = 10 const actual = 3 expect(expected > actual).toEqual(true) }) test('expected is greater than the desired', () => { const expected = 10 const actual = 3 expect(expected).toBeGreaterThan(actual) }) Booleans 🧱
  24. test('expected is greater than the desired', () => { const

    expected = 10 const actual = 3 expect(expected > actual).toEqual(true) }) test('expected is greater than the desired', () => { const expected = 10 const actual = 3 expect(expected).toBeGreaterThan(actual) }) Booleans 🧱
  25. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Arrays 🧱
  26. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Arrays 🧱
  27. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Arrays 🧱
  28. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual( expect.arrayContaining(actualFruits) ) }) Arrays 🧱
  29. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual(expect.arrayContaining(actualFruits)) }) Arrays 🧱
  30. test('list three fruits', () => { const expectedFruits = ['banana',

    'mango', 'watermelon'] expect(expectedFruits[0]).toEqual('banana') expect(expectedFruits[1]).toEqual('mango') expect(expectedFruits[2]).toEqual('watermelon') }) test('list three fruits', () => { const expectedFruits = ['banana', 'mango', 'watermelon'] const actualFruits = ['banana', 'mango', 'watermelon'] expect(expectedFruits).toEqual(expect.arrayContaining(actualFruits)) }) Arrays 🧱
  31. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  32. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  33. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  34. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  35. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  36. test('list with three numbers', () => { const myList =

    [1, 2, 3] expect(myList.length).toEqual(3) }) test('list with three numbers', () => { const myList = [1, 2, 3] expect(myList).toHaveLength(3) }) Arrays 🧱
  37. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  38. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  39. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  40. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  41. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  42. test('light is on', () => { const isOff = false

    expect(!isOff).toBe(true) }) test('light is on', () => { const isOff = false expect(isOff).not.toBe(true) }) Not 🪣
  43. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  44. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  45. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  46. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  47. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  48. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  49. test('callback has been invoked', done => { callAsyncFunc(() => {

    expect(true).toEqual(true) done() }) }) test('callback has been invoked', () => { const result = jest.fn() callAsyncFunc(result) expect(result).toHaveBeenCalled() }) Callbacks 📱 const callAsyncFunc = cb => cb();
  50. function callAsyncFunction() { return new Promise((resolve) => { setTimeout(() =>

    { resolve(true); }, 1000); }); } test('assert after some time', done => { callAsyncFunction(). then((value) => { expect(value).toBe(true) done() }) }) Timers ⏰
  51. function callAsyncFunction() { return new Promise((resolve) => { setTimeout(() =>

    { resolve(true); }, 1000); }); } test('assert after some time', done => { callAsyncFunc(). then((value) => { expect(value).toBe(true) done() }) }) Timers ⏰
  52. function callAsyncFunction() { return new Promise((resolve) => { setTimeout(() =>

    { resolve(true); }, 1000); }); } test('assert after some time', done => { callAsyncFunction(). then((value) => { expect(value).toBe(true) done() }) }) Timers ⏰
  53. describe('using fake timers', () => { beforeEach(() => { jest.useFakeTimers()

    }) afterEach(() => { jest.useRealTimers() }) }) Timers ⏰
  54. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  55. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  56. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  57. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  58. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  59. test("should handle next scene", async () => { const value

    = callAsyncFunction(); jest.advanceTimersByTime(2000); expect(await value).toBe(true); }); Timers ⏰
  60. test('my async test with then', done => { callAsyncFunction(). then((value)

    => { expect(value).toBe(true) done() }) }) test('my async test', async () => { await expect(callAsyncFunction()).resolves.toEqual(true) }) test('my async test rejects', async () => { await expect(rejectsAsyncFunction()).rejects.toEqual(false) }) Promises 🏹
  61. test('my async test with then', done => { callAsyncFunction(). then((value)

    => { expect(value).toBe(true) done() }) }) test('my async test', async () => { await expect(callAsyncFunction()).resolves.toEqual(true) }) test('my async test rejects', async () => { await expect(rejectsAsyncFunction()).rejects.toEqual(false) }) Promises 🏹
  62. test('my async test with then', done => { callAsyncFunction(). then((value)

    => { expect(value).toBe(true) done() }) }) test('my async test', async () => { await expect(callAsyncFunction()).resolves.toEqual(true) }) test('my async test rejects', async () => { await expect(rejectsAsyncFunction()).rejects.toEqual(false) }) Promises 🏹
  63. test('my async test with then', done => { callAsyncFunction(). then((value)

    => { expect(value).toBe(true) done() }) }) test('my async test', async () => { await expect(callAsyncFunction()).resolves.toEqual(true) }) test('my async test rejects', async () => { await expect(rejectsAsyncFunction()).rejects.toEqual(false) }) Promises 🏹
  64. test('my async test with then', done => { callAsyncFunction(). then((value)

    => { expect(value).toBe(true) done() }) }) test('my async test', async () => { await expect(callAsyncFunction()).resolves.toEqual(true) }) test('my async test rejects', async () => { await expect(rejectsAsyncFunction()).rejects.toEqual(false) }) Promises 🏹
  65. • toEqual is usually used more than any assertion •

    Assertion smells • Using different assertions can improve understanding • 🧱, 🪣,📱, ⏰, 🏹 What we covered
  66. Matheus Marabesi Hello there, you can call me Marabesi, But

    my name is Matheus Marabesi, I work at Codurance as a Software craftsperson. I enjoy talking about anything related to: testing, patterns and gamification. You can find me at @MatheusMarabesi or https://marabesi.com Codurance Crafting Code