CodeRetreat

Today is about learning & practicing

Today is about learning & practicing

What is automated testing?

  • A brief code snippet to verify that other codes behave as expected

  • Automatic, repeatable

  • Fast & deterministic

  • Code's test is the test, the test's test is the code

Test-Driven Development

A goal is Clean Code!

  1. Don’t write a line of new code unless you first have a failing automated test

  2. Eliminate duplication

Two rules of TDD

by Kent Beck

  1. Write a test…

  2. Make it run…

  3. Make it right…

A goal is loose coupling!

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.

  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.

  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Three rules of TDD

by Uncle Bob Martin

A goal is documenting!

  1. Write a test for the next bit of functionality you want to add

  2. Write the functional code until the test passes

  3. Refactor both new and old code to make it well structured

Three rules of TDD

by Martin Fowler

A goal is easy refactoring!

About TDD

by James Shore

A goal is fast cycles via small steps!

Test Driven Development

by Draven

A goal is planning and

experimenting in the code

A goal is to build trust
in the codebase

Coding Rules for Today

  • Strictly follow TDD rules and do fast cycles

  • Don't forget refactoring

  • As delivery doesn't matter, focus on readable Clean Code

  • Test names and bodies must document the codebase

  • There is no need for external dependencies, mocking, creating UI, etc.

TDD RULZ

Coderetreat

TDD RULZ

Coderetreat

Game Of Life

Game Of Life

  • Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.

  • Any live cell with more than three live neighbours dies, as if by overcrowding.

  • Any live cell with two or three live neighbours lives on to the next generation.

  • Any dead cell with exactly three live neighbours becomes a live cell.

SESSION #1

SESSION #1

No constraint, just do
TDD and Clean Code

SESSION #2

Good tests
document the code

  • Test cases against behaviour

  • Test naming

    • Include the method under testing

    • Add context, preconditions

    • Write what will happen, the expected behaviour

describe("Math", () => {
	describe("#add", () => {
    	it("should return 3 if 1 and 2 are given", () => {
        	expect(add(1,2)).toEqual(3)
describe("Math", () => {
	describe("#add", () => {
    	it("should return the sum of two numbers given", () => {
        	expect(add(0,1)).toEqual(1)
			expect(add(1,2)).toEqual(3)

Bad:

Better:

describe("Given the balance is 1.000 €", () => {
  describe("When making a deposit of 100 €", () => {
    it("Then I expect the balance to be 1.100 €", () => ...
describe("Given an initial balance in the account", () => {
  describe("When making a deposit", () => {
    it("Should add the deposit to the balance", () => ...

Bad:

Better:

SESSION #2

Constraints:

  1. TDD Ping-Pong

  2. Behavior in test names

  3. Bonus: infinite table

SESSION #3

TDD Strategies

Obvious Implementation
Fake It ('til you make it)
Triangulation

TDD Strategies

Obvious Implementation

describe("#getSquareArea", () => {
  it("should return the area of a square with the given edge length", () => {
    const area = getSquareArea(3)
    expect(area).toEqual(9)
  })
})

function getSquareArea(edgeLength: Number): Number {
  return edgeLength * edgeLength
}

TDD Strategies

Fake it ('til you make it)

describe("#getSquareArea", () => {
  it("should return the area of ...", () => {
    expect(getSquareArea(3)).toEqual(9)
  })
})

// Iteration 1
function getSquareArea(edgeLength: Number): Number {
  return 9
}

// Iteration 2
function getSquareArea(edgeLength: Number): Number {
  return 3 * 3
}

// Iteration 3
function getSquareArea(edgeLength: Number): Number {
  return edgeLength * edgeLength
}

TDD Strategies

Triangulation

describe("#getSquareArea", () => {
  it("should return the area of ...", () => {
    expect(getSquareArea(0)).toEqual(0)
  })
})

function getSquareArea(edgeLength: Number): Number {
  return 0
}
describe("#getSquareArea", () => {
  it("should return the area of ...", () => {
    expect(getSquareArea(0)).toEqual(0)
    expect(getSquareArea(1)).toEqual(1)
  })
})

function getSquareArea(edgeLength: Number): Number {
  return edgeLength
}
describe("#getSquareArea", () => {
  it("should return the area of ...", () => {
    expect(getSquareArea(0)).toEqual(0)
    expect(getSquareArea(1)).toEqual(1)
    expect(getSquareArea(2)).toEqual(4)
  })
})

function getSquareArea(edgeLength: Number): Number {
  return edgeLength * edgeLength
}

SESSION #3

Constraints:

  1. TDD Ping-Pong

  2. Behavior in test names

  3. Inifinite table

  4. Baby Steps

SESSION #4

All About Clean Code

Make your code easy to understand!

  • Naming:

    • Use descriptive, unambigious names

    • Replace magic number with named constants

  • Functions

    • Small & do one thing

    • Descriptive name

    • No side effects

    • Don't use flag arguments, create separate functions

Law of Demeter

"A method of an object should only call
methods on objects that are directly related to it"

Law of Demeter

"A method of an object should only call
methods on objects that are directly related to it"

class Piston {
    move() {}
}

class Engine {
    constructor(this.piston: Piston) { this.piston = new Piston() }

    getPiston(): Piston {
        return this.piston;
    }
}

class Car {
    constructor(this.engine: Engine) { this.engine = new Engine() }

    start() {
        this.engine.getPiston().move();
    }
}

Law of Demeter

"A method of an object should only call
methods on objects that are directly related to it"

class Piston {
    move() {}
}

class Engine {
    constructor(this.piston: Piston) { this.piston = new Piston() }

    start(): Piston {
        return this.piston.move();
    }
}

class Car {
    constructor(this.engine: Engine) { this.engine = new Engine() }

    start() {
        this.engine.start();
    }
}

SESSION #4

Constraints:

  1. First 5 minutes planning!

  2. Small functions (max 3 lines)

  3. No primitives

  4. Bonus: No loop (for/while)

SESSION #5

SESSION #5

Constraints:

-

SESSION #6