TDD, BDD and mocks
Upcoming SlideShare
Loading in...5
×
 

TDD, BDD and mocks

on

  • 13,304 views

 

Statistics

Views

Total Views
13,304
Views on SlideShare
10,698
Embed Views
2,606

Actions

Likes
26
Downloads
352
Comments
1

9 Embeds 2,606

http://kingori.egloos.com 2181
http://sakula99.egloos.com 365
http://www.slideshare.net 33
http://andstudy.com 22
http://www.andstudy.com 1
http://www.parkpd.x-y.net 1
http://74.125.153.132 1
http://bit.ly 1
http://www.linkedin.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Very good slice! cool
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

TDD, BDD and mocks TDD, BDD and mocks Presentation Transcript

  • Test- Behaviour- } Driven Development Kerry Buckley
  • Clearing up some Misconceptions
  • TDD is not about testing
  • TDD does not mean handing acceptance tests to developers
  • TDD is a design activity
  • Software design is emergent, and happens during development
  • Why TDD? • Makes you think about required behaviour • Reduces speculative code • Provides documentation • Improves quality
  • Evolution
  • Dirty Hacking
  • Automated Testing
  • Automated Testing class Adder def add a, b a + b end end 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) end end
  • Are You Really Testing Your Code? class Adder def add a, b a + b end end class AdderTest < Test::Unit::TestCase def test_add assert_equal 4, 2 + 2 end end
  • Test-First Development
  • Test-First Development Write Write tests code Start Failing Done tests
  • Test-Driven Development
  • Test-Driven Development Clean Failing code test Refactor All tests pass
  • State-Based class DongleTest < Test::Unit::TestCase def test_wibble # Set up test inputs dongle = Dongle.new dongle.addString(quot;fooquot;) dongle.addRemoteResource(quot;http://foo.com/barquot;) # Exercise functionality under test dongle.wibble! # Verify results are as expected assert_equal(42, dongle.answer) end end
  • Bottom-Up
  • Behaviour-Driven Development
  • Behaviour-Driven Development Verification Specification State-based Interaction-based Bottom-up Outside-in Testing tool Design tool Invention Discovery
  • 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) end end
  • RSpec describe quot;An adderquot; do it quot;should add two positive numbersquot; do Adder.new.add(2, 2).should == 4 end it quot;should add a positive and a negative numberquot; do Adder.new.add(4, -2).should == 2 end end
  • 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
  • Matchers (RSpec) @string.should == quot;fooquot; @array.should_not be_empty @hash.should have_key(:foo) @object.should be_an_instance_of String lambda { @stack.pop }.should raise_error(StackUnderflowError)
  • Matchers (HamCrest) assertThat(string, equalTo(quot;fooquot;)); assertThat(array, hasItem(quot;barquot;)); assertThat(obj, instanceOf(String.class)); assertThat(number, greaterThan(42));
  • Outside-In
  • Integration Testing
  • 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
  • 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_i end Then /^the '(w*)' account should contain £(d*)$/ do |name, amount| @@accounts[name].balance.should == amount.to_i end
  • Unit Testing
  • Interaction-Based
  • Mock Objects Mock Mock
  • Classicists v Mockists
  • 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!
  • Boundary Objects
  • Mocking Patterns • Record and playback • Specify expectations before running • Check expectations after running
  • Mocking Patterns class 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 end end
  • EasyMock public 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); }
  • JMock Mockery 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(); }
  • 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); }
  • 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 end end
  • Not-a-Mock describe '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) end end
  • 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
  • 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!
  • 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!
  • Further Reading Introducing BDD (Dan North) http://dannorth.net/introducing-bdd BDD Introduction http://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