TDD

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    Notes on slide 1

    Short presentation on the background and principles of TDD and BDD.

    Think about what the code you’re writing is supposed to do. Reduces temptation to write speculative code. Executable docs. Forces clean structure (or at least makes poor structure painful).

    Think about what the code you’re writing is supposed to do. Think about interfaces and responsibilities. Think about alternate paths (errors etc).

    Reduces temptation to write speculative code.

    Executable docs. (Almost) guaranteed up-to-date.

    Forces clean structure (or at least makes poor structure painful).
    Catches some regression bugs.
    Makes refactoring safer (meaning you’re more likely to do it).

    Tests help catch regression problems, enable rapid deployment and give you the confidence to refactor. But that’s just a benefit of unit testing, not TDD.

    Not just the evolution of the practice, but steps most people go through in their understanding of TDD.

    We’ve all written code this way, and probably revert to it from time to time!
    Manual testing (either by the developer or someone else).
    Works at a small scale and in the short term, but unmaintainable.

    Write test cases to prove that the code works.
    Tests provide confidence that we haven’t broken anything when making changes.

    Classic (but not recommended) approach is to create one test case per production class, and one test method per production method.

    This passes, but doesn’t actually call our method. Obviously it’s an extreme example, but it illustrates the problem of writing test cases when the code’s already there.

    How do we know the tests are correct?
    Write them first, watch them fail, then write the code and check they pass.

    This is an improvement, but means you have to have a fairly complete design before you start (or you are restricted to system-level integration tests, rather than unit testing each class/method.

    This is an improvement, but means you have to have a fairly complete design before you start (or you are restricted to system-level integration tests, rather than unit testing each class/method.

    This is an improvement, but means you have to have a fairly complete design before you start (or you are restricted to system-level integration tests, rather than unit testing each class/method.

    Subtle difference from test-first: it’s about design more than testing.
    Make small changes, only writing enough to pass a test.
    After each test, refactor code to a clean design.

    Cycle should repeat at least every few minutes. Sometimes several times per minute.

    Cycle should repeat at least every few minutes. Sometimes several times per minute.

    Cycle should repeat at least every few minutes. Sometimes several times per minute.

    Cycle should repeat at least every few minutes. Sometimes several times per minute.

    Cycle should repeat at least every few minutes. Sometimes several times per minute.

    To a large extent, BDD is ‘TDD done well’.

    We’re now specifying what the object under test should do, rather than just writing an uninformatively-named method to test it.

    This is RSpec, a framework specifically designed for BDD.

    Even if you’re only using an xUnit framework, you can extract similar information if you give your test methods descriptive names.

    In case the Java people were feeling left-out.

    Rather than guessing what low-level objects we need and building the system up from there, we start with the actual requirement at the outside of the system (user inter, and discover the need for lower-level objects as we go.

    Integration testing uses a state-based approach. Used for acceptance tests for new features, which then live on as regression tests.

    Cucumber. Now also available for Java (Cuke4Duke), but see also JBehave and EasyB.

    Test classes in isolation.

    When we ask the object to perform a specific action, it should interact in a particular way with its collaborators. But how do we make that test pass if the collaborator classes haven’t been written yet?

    Mocks and stubs.

    Stubs are used to mimic the behaviour of other parts of the system that you don’t want to use for real. Mocks are used to specify interactions. Don’t use mocks for library calls etc which you can’t control – create a thin wrapper instead, and mock calls to that (let the mocks drive the design of the wrapper API).

    Boundary objects can be tested using a state-based approach, as mocks are obviously not applicable here.

    Naive example! Also you wouldn’t really have this code when you wrote the tests/specs.

    Record/playback. Advantage: IDE/refactoring support. Disadvantage: doesn’t necessarily encourage writing the test first.

    Specify expectations in advance, then verify afterwards. Advantage: more declarative. Disadvantage: Weird syntax.

    Specify expectations in advance. Verified automatically.

    Also known as test spies.

    Calling methods on mocks is silent. Verify that expected methods were called afterwards.

    Alternative mocking framework for RSpec. Stub methods out first (on real instances), then verify that expected methods were called afterwards.

    Testing behaviour means calling public methods and observing what the class under test does. You should not need to use tricks to test private methods or inspect instance variables.

    Tests should be runnable individually and in any order. If one test depends on data set up by another, this will make it hard to maintain your test suite, and cause puzzling failures. This is what setup and teardown methods are for.

    If each test only asserts one aspect of behaviour, it makes it obvious what’s wrong when it fails. It also prevents the first failure from masking later assertions.

    Use stubs and mocks to decouple the class under test from the rest of the system.
    If the method under test is relying on a collaborator providing data, that can simply be stubbed – there’s no need to assert that the query happened. Use mocks to test that the code under test is passing the right messages to its collaborators.
    Beware of going overboard with stubs and mocks. There’s a happy medium between not using them at all and over-using them.
    Don’t use stubs and mocks in integration tests (other than possibly for interaction with other systems).

    Listen to the tests. If they’re hard to write or brittle, it’s probably a sign that the design of the code could be improved.

    2 Favorites

    TDD - Presentation Transcript

    1. Test-Driven Development Kerry Buckley
    2. Clearing up some Misconceptions
    3. It’s all about testing
    4. G ! ON It’s all about testing W R
    5. Tests handed to coders
    6. R ! E Tests handed G coders O N to W R
    7. Design activity
    8. Design is emergent
    9. Tests drive that design
    10. Why?
    11. Makes you think
    12. Focus
    13. Documentation
    14. Quality
    15. …and tests
    16. Evolution
    17. Dirty Hacking
    18. Automated Testing
    19. 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
    20. 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
    21. Test-First Development
    22. Test-First Development
    23. Test-First Development Start
    24. Test-First Development Write tests Start Failing tests
    25. Test-First Development Write Write tests code Start Failing Done tests
    26. Test-Driven Development
    27. Test-Driven Development
    28. Test-Driven Development Clean code
    29. Test-Driven Development Clean Failing code test
    30. Test-Driven Development Clean Failing code test All tests pass
    31. Test-Driven Development Clean Failing code test Refactor All tests pass
    32. Test-Driven Development Clean Failing code test Refactor All tests pass
    33. Three Rules http://tinyurl.com/threerules
    34. Three Rules 1. Do not write any production code unless it is to make a failing unit test pass. http://tinyurl.com/threerules
    35. Three Rules 1. Do not write any production code unless it is to make a failing unit test pass. 2. Do not write any more of a unit test than is sufficient to fail; and compilation failures are failures. http://tinyurl.com/threerules
    36. Three Rules 1. Do not write any production code unless it is to make a failing unit test pass. 2. Do not write any more of a unit test than is sufficient to fail; and compilation failures are failures. 3. Do not write any more production code than is sufficient to pass the one failing unit test. http://tinyurl.com/threerules
    37. State-Based class 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)   end end
    38. Bottom-Up
    39. Behaviour-Driven Development
    40. 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
    41. RSpec describe 'An adder' do   it 'can add two positive numbers' do     Adder.new.add(2, 2).should == 4   end   it 'can add a positive and a negative number' do     Adder.new.add(4, -2).should == 2   end end
    42. Generated Documentation $ spec -f s adder_spec.rb An adder - can add two positive numbers - can add a positive and a negative number Finished in 0.005493 seconds 2 examples, 0 failures
    43. 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)
    44. Matchers (HamCrest) assertThat(string, equalTo("foo")); assertThat(array, hasItem("bar")); assertThat(obj, instanceOf(String.class)); assertThat(number, greaterThan(42));
    45. Outside-In
    46. Integration Testing
    47. 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
    48. Step Implementations 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
    49. Unit Testing
    50. Interaction-Based
    51. Fake Objects © Matthew Lee High http://www.flickr.com/photos/matthigh/3045096094/
    52. Mocks and Stubs Stub Mock
    53. Boundary Objects
    54. Mocking Patterns
    55. class AccountController   def transfer from, to, amount     from.debit amount     to.credit amount   end end class AccountController {   public void transfer(Account from, Account  to, int amount) {     from.debit(amount);     to.credit(amount);   } }
    56. Record → Playback → Run → Verify
    57. EasyMock public void testTransferDebitsSourceAccount() {   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); }
    58. Expect → Run → Verify
    59. JMock public void testTransferDebitsSourceAccount() {   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(); }
    60. RSpec describe 'Making a transfer' do   it 'debits the source account' do     controller = AccountController.new     from = mock 'from'     to = mock 'to'     to.stub :credit          from.should_receive(:debit).with 42          controller.transfer from, to, 42   end end
    61. Stub → Run → Verify
    62. Mockito public void testTransferDebitsSourceAccount() {   AccountController controller = new AccountController();      Account from = mock(Account.class);   Account to = mock(Account.class);   controller.transfer(from, to, 42);   verify(from).debit(42); }
    63. Not-a-Mock describe 'Making a transfer' do   it 'debits 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
    64. Other Mock Features
    65. Mix stubs and mocks describe 'Peter Petrelli' do   before do     @peter = PeterPetrelli.instance     @claire = mock Cheerleader     @world = mock World     @claire.stub :save     @world.stub :save   end   it 'saves the cheerleader' do     @claire.should_receive :save     @peter.fulfil_destiny   end   it 'saves the world' do     @world.should_receive :save     @peter.fulfil_destiny   end end
    66. Specify return values @object.stub(:foo).and_return 2, 4, 6 @object.foo # => 2 @object.foo # => 4 @object.foo # => 6 @object.foo # => 6
    67. Mock or stub class methods @now = Time.now Time.stub(:now).and_return @now @object.should_receive(:update_timestamp).with @now
    68. Specify expected number of calls @object.should_receive(:foo).once @object.should_receive(:bar).at_least(3).times
    69. Raise exceptions @object.stub(:foo).and_raise RuntimeError, 'message'
    70. Good Practices
    71. Test Behaviour, not Implementation
    72. Keep Tests Independent
    73. One Assertion/ Expectation per Test
    74. Stub and Mock Where Appropriate
    75. If Testing is Hard, the Design is Probably Wrong
    76. 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

    + Kerry BuckleyKerry Buckley, 2 months ago

    custom

    261 views, 2 favs, 0 embeds more stats

    Yet another rehash of the same old TDD/BDD presenta more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 261
      • 261 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 2
    • Downloads 10
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories

    Tags