tdd, bdd and mocks

46
Test- Behaviour- Kerry Buckley Driven Development }

Upload: kerry-buckley

Post on 10-May-2015

11.051 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: TDD, BDD and mocks

Test-Behaviour-

Kerry Buckley

Driven Development}

Page 2: TDD, BDD and mocks

Clearing up some Misconceptions

Page 3: TDD, BDD and mocks

TDD is not about testing

Page 4: TDD, BDD and mocks

TDD does not mean handing acceptance tests to developers

Page 5: TDD, BDD and mocks

TDD is a design activity

Page 6: TDD, BDD and mocks

Software design is emergent, and happens

during development

Page 7: TDD, BDD and mocks

Why TDD?

• Makes you think about required behaviour

• Reduces speculative code

• Provides documentation

• Improves quality

Page 8: TDD, BDD and mocks

Evolution

Page 9: TDD, BDD and mocks

Dirty Hacking

Page 10: TDD, BDD and mocks

Automated Testing

Page 11: TDD, BDD and mocks

Automated Testingclass Adder def add a, b a + b endend

class AdderTest < Test::Unit::TestCase def test_add adder = Adder.new assert_equal 4, adder.add(2, 2) assert_equal 2, adder.add(4, -2) endend

Page 12: TDD, BDD and mocks

Are You Really Testing Your Code?

class Adder def add a, b a + b endend

class AdderTest < Test::Unit::TestCase def test_add assert_equal 4, 2 + 2 endend

Page 13: TDD, BDD and mocks

Test-First Development

Page 14: TDD, BDD and mocks

Test-First Development

Failingtests

Start Done

Writecode

Writetests

Page 15: TDD, BDD and mocks

Test-Driven Development

Page 16: TDD, BDD and mocks

Test-Driven Development

Failingtest

Cleancode

All tests pass

Refactor

Page 17: TDD, BDD and mocks

State-Basedclass DongleTest < Test::Unit::TestCase def test_wibble # Set up test inputs dongle = Dongle.new dongle.addString("foo") dongle.addRemoteResource("http://foo.com/bar") # Exercise functionality under test dongle.wibble! # Verify results are as expected assert_equal(42, dongle.answer) endend

Page 18: TDD, BDD and mocks

Bottom-Up

Page 19: TDD, BDD and mocks

Behaviour-Driven Development

Page 20: TDD, BDD and mocks

Behaviour-Driven Development

Verification Specification

State-based Interaction-based

Bottom-up Outside-in

Testing tool Design tool

Invention Discovery

Page 21: TDD, BDD and mocks

More Descriptive Test Names

class AdderTest < Test::Unit::TestCase def test_should_add_two_positive_numbers assert_equal 4, Adder.new.add(2, 2) end

def test_should_add_a_positive_and_a_negative_number assert_equal 2, Adder.new.add(4, -2) endend

Page 22: TDD, BDD and mocks

RSpec

describe "An adder" do it "should add two positive numbers" do Adder.new.add(2, 2).should == 4 end

it "should add a positive and a negative number" do Adder.new.add(4, -2).should == 2 endend

Page 23: TDD, BDD and mocks

Generated Documentation

$ spec -f s adder_spec.rb

An adder- should add two positive numbers- should add a positive and a negative number

Finished in 0.005493 seconds

2 examples, 0 failures

Page 24: TDD, BDD and mocks

Matchers (RSpec)

@string.should == "foo"

@array.should_not be_empty

@hash.should have_key(:foo)

@object.should be_an_instance_of String

lambda { @stack.pop }.should raise_error(StackUnderflowError)

Page 25: TDD, BDD and mocks

Matchers (HamCrest)

assertThat(string, equalTo("foo"));

assertThat(array, hasItem("bar"));

assertThat(obj, instanceOf(String.class));

assertThat(number, greaterThan(42));

Page 26: TDD, BDD and mocks

Outside-In

Page 27: TDD, BDD and mocks

Integration Testing

Page 28: TDD, BDD and mocks

Describing Features

Feature: Transferring money between two accounts

Scenario: Simple transfer Given an account called 'source' containing £100 And an account called 'destination' containing £50 When I transfer £20 from source to destination Then the 'source' account should contain £80 And the 'destination' account should contain £70

Page 29: TDD, BDD and mocks

Describing Features

Given /^an account called '(\w*)' containing £(\d*)$/ do |name, amount| @@accounts ||= {} @@accounts[name] = Account.new(amount.to_i)end

When /^I transfer £(\d*) from (\w*) to (\w*)$/ do |amount, from, to| AccountController.new.transfer @@accounts[from], @@accounts[to], amount.to_iend

Then /^the '(\w*)' account should contain £(\d*)$/ do |name, amount| @@accounts[name].balance.should == amount.to_iend

Page 30: TDD, BDD and mocks

Unit Testing

Page 31: TDD, BDD and mocks

Interaction-Based

Page 32: TDD, BDD and mocks

Mock Objects

Mock Mock

Page 33: TDD, BDD and mocks

Classicists v Mockists

Page 34: TDD, BDD and mocks

Mock Objects

• Stand-ins for collaborating objects

• Mock the interface, not a specific object

• Verify that expected calls are made

• Not stubs!

• For your code only!

Page 35: TDD, BDD and mocks

Boundary Objects

Page 36: TDD, BDD and mocks

Mocking Patterns

• Record and playback

• Specify expectations before running

• Check expectations after running

Page 37: TDD, BDD and mocks

Mocking Patternsclass AccountController { public void transfer(Account from, Account to, int amount) { from.debit(amount); to.credit(amount); }}

class AccountController def transfer from, to, amount from.debit amount to.credit amount endend

Page 38: TDD, BDD and mocks

EasyMockpublic void testTransferShouldDebitSourceAccount() { AccountController controller = new AccountController();

Account from = createMock(Account.class); Account to = createNiceMock(Account.class); from.debit(42);

replay(from); replay(to);

controller.transfer(from, to, 42);

verify(from);}

Page 39: TDD, BDD and mocks

JMockMockery context = new Mockery();

public void testTransferShouldDebitSourceAccount() { AccountController controller = new AccountController(); Account from = context.mock(Account.class); Account to = context.mock(Account.class);

context.checking(new Expectations() {{ oneOf(from).debit(42); }});

controller.transfer(from, to, 42);

context.assertIsSatisfied();}

Page 40: TDD, BDD and mocks

Mockito

public void testTransferShouldDebitSourceAccount() { AccountController controller = new AccountController(); Account from = mock(Account.class); Account to = mock(Account.class);

controller.transfer(from, to, 42);

verify(from).debit(42);}

Page 41: TDD, BDD and mocks

RSpec

describe 'Making a transfer' do it 'should debit the source account' do controller = AccountController.new

from = stub_everything 'from' to = stub_everything 'to' from.should_receive(:debit).with 42 controller.transfer from, to, 42 endend

Page 42: TDD, BDD and mocks

Not-a-Mockdescribe 'Making a transfer' do it 'should debit the source account' do controller = AccountController.new from = Account.new to = Account.new from.stub_method :debit => nil to.stub_method :credit => nil controller.transfer from, to, 42 from.should_have_received(:debit).with(42) endend

Page 43: TDD, BDD and mocks

Other Mock Features

• Stubs (for when you don’t care)

• Mock class/static methods

• Specify return values

• Specify expected number of calls

• Specify method ordering

• Raise exceptions on method calls

Page 44: TDD, BDD and mocks

Good Practices• Test behaviour, not implementation

• One expectation per test

• Don’t test private methods

• Don’t mock everything

• Stub queries; mock actions

• Tell, don’t ask

• Listen to test smells!

Page 45: TDD, BDD and mocks

TDD/BDD Summary• Never write any code without a failing test

• Start from the outside, with acceptance tests

• Drive design inwards using mock objects

• Tests should be descriptive specifications

• Red – Green – Refactor

• YAGNI

• TATFT!

Page 46: TDD, BDD and mocks

Further ReadingIntroducing BDD (Dan North)

http://dannorth.net/introducing-bdd

BDD Introductionhttp://behaviour-driven.org/Introduction

Mock Roles, Not Objects(Freeman, Mackinnon, Pryce, Walnes)

http://www.jmock.org/oopsla2004.pdf

BDD in Ruby (Dave Astels)http://blog.daveastels.com/files/BDD_Intro.pdf

Test all the F***in Time (Brian Liles, video)http://www.icanhaz.com/tatft