Test and Behaviour Driven Development (TDD/BDD)
-
Upload
lars-thorup -
Category
Technology
-
view
10.386 -
download
2
description
Transcript of Test and Behaviour Driven Development (TDD/BDD)
Test & BehaviourDriven DevelopmentLars ThorupZeaLake Software Consulting
March, 2012
Who is Lars Thorup?
● Software developer/architect● C++, C# and JavaScript● Test Driven Development
● Coach: Teaching agile and automated testing
● Advisor: Assesses software projects and companies
● Founder and CEO of BestBrains and ZeaLake
● What is TDD/BDD?● Express expected behaviour before writing code
● Why is TDD/BDD a good thing?● Enjoy more efficient and predictable course of development● Find and fix bugs faster● Prevent bugs from reappearing● Improve the design of our software● Reliable documentation
● How do we do TDD/BDD?● Write test programs● Run the tests automatically
Why are we here today?
Workflow of TDD/BDD
Failingtest
Succeedingtest
Gooddesign Refactor
Code
Test
IdeaThink, talk
BDD or TDD?● Behaviour first
● makes more sense than "Test first"
● Structure of test programs● Given <precondition>● When <invocation>● Then <expectation>
● High level as well as low level● Testing user stories and requirements● Testing class design and algorithms
● Communicate intent
● Fast feedback
Different kinds of automated tests● Unit tests
● Test individual pieces of code and the interaction between code blocks
● System tests / acceptance tests● Verify the behaviour of the entire system against the requirements
● Performance tests● Test non functional requirements
Unit tests or system tests?● Unit tests are efficient
● Fast to run (hundreds per second)● Robust and predictable● Can be easy to write● Is written together with the code it is testing
● System tests are thorough● Tests all layers together● Most efficient way to create a set of tests for existing code● Can be easier to read for non-technical people
Can we automate performance tests?● Performance tests are brittle
● Tip: create performance trend curves instead
How do we run the tests automatically?● From our programming environment (IDE)
● Command line: make test● Right click | Run Tests
● On every commit● Setup a build server● Jenkins, TeamCity● Let the build server run all tests● Get build notifications● Keep the build green● Fixing a broken build has priority over any other development task
How can tests help improve our design?● The software design will evolve over time
● A refactoring improves the design without changing behavior
● Tests ensure that behavior is not accidentally changed
● Without tests, refactoring is scary● and with no refactoring, the design decays over time
● With tests, we have the courage to refactor● so we continually keep our design healthy
Are we wasting developer time writing tests?● No
● Time spent writing tests is not taken from time spent coding● ... but from time otherwise spent on manual testing and debugging
● The cost of a bug keeps increasing until we fix it
● Find bugs faster● Avoid losing customer confidence● Free QA to do exploratory testing
so they find the hard-to-find bugs● Spend less time trying to figure out
what is causing the bug and how to fix it
● Avoid spending time testing again
How do we get started?● When we have a lot of existing code without tests
● Create a set of system tests to get a safety net
● When we are writing new code● Write unit tests in conjunction with the new code
● Set up a standard test environment for our specific application● Test data: Automate the creation of standard testdata in a local
database● External dependencies: Write stubs to use in the tests
Maintainability● Stick to a pattern for your tests
● E.g. Given-When-Then
● Focus on readability over code duplication in test code
● Write reusable helper classes (builders) to simplify tests
● wizerize.com● Web application: C# and JavaScript● 3½ years of development, 3½ years in production● 2-4 developers● 40% test code, 60% production code (in lines of code)● 71% code coverage of unit tests● 872 unit tests – run in 1½ minute● 72 system tests – run in 20 minutes● No functional errors seen by end users in production (yet)
What does a real-world project look like?
Where can I read more?● http://googletesting.blogspot.com/
● http://testdrivendeveloper.com/
● http://codesheriff.blogspot.com/
● http://www.zealake.com/category/test/
But what about:● Stubs & mocks
● Test data
● UI testing
● SQL testing
● JavaScript testing
● Web Service testing
● Legacy code
What is good design?● One element of good design is loose coupling
● Use interfaces (for static languages)● Inject dependencies
● Avoid using new:
● Inject dependencies instead:private IEmailSvc emailSvc;public Notifier(IEmailSvc emailSvc){ this.emailSvc = emailSvc;}
public void Trigger(){ emailSvc.SendEmail();
public void Trigger(){ var emailSvc = new EmailSvc(); emailSvc.SendEmail();}
Stubs and mocks● When testing an object X, that depends on an object Y
● replace the real Y with a fake Y
● Benefits● Only test one thing (X) at a time● Faster tests (Y may be slow)● Simpler (Y may depend on Z etc)
● Examples:● Time● Database● Email● HttpContext
Notifier
EmailSvc
IEmailSvc
EmailSvcStub
NotifierTest
Stubs● Hand crafted
● More effort to write
● Easier to maintain
● Can be more "black box" than mocks
Mocks● Mocks are automatically generated stubs
● Easy to use
● More "magical"
● More effort to maintain
● Will be more "white-box" than stubs
● Example frameworks:● Moq● NSubstitute
Stubs - examplepublic class EmailSvcStub : IEmailSvc{ public int NumberOfEmailsSent { get; set; }
public void SendEmail() { ++NumberOfEmailsSent; }}
[Test]public void Trigger(){ // setup var emailSvc = new EmailSvcStub(); var notifier = new Notifier(emailSvc);
// invoke notifier.Trigger();
// verify Assert.That(emailSvc.NumberOfEmailsSent, Is.EqualTo(1));}
Mocks - example
[Test]public void Trigger(){ // setup var emailSvc = Substitute.For<IEmailSvc>(); var notifier = new Notifier(emailSvc);
// invoke notifier.Trigger();
// verify emailSvc.Received(1).SendEmail();}
Test data● Each developer his/her own database
● Standard test data● Created before running tests
● Test data builders● Stubbed database● Real database
Test data builder - example[Test]public void GetResponseMedia(){ // given var stub = new StubBuilder { Questions = new [] { new QuestionBuilder { Name = "MEDIA" }, }, Participants = new[] { new ParticipantBuilder { Name = "Lars", Votes = new [] { new VoteBuilder { Question = "MEDIA", Responses = new ResponseBuilder(new byte [] {1, 2, 3}) }, }}, }, }.Build(); var voteController = new VoteController(stub.Session);
// when var result = voteController.GetResponseMedia(vote.Id, true) as MediaResult;
// then Assert.That(result.Download, Is.True); Assert.That(result.MediaLength, Is.EqualTo(3)); Assert.That(TfResponse.ReadAllBytes(result.MediaStream), Is.EqualTo(new byte[] {1, 2, 3}));}
Web UI testing● Control a browser from the tests using a seperate tool
● Tools● Selenium● WatiN● Cucumber + capybara
● Minimize system level testing● Web UI tests are brittle and slow● Hard to integrate into continuous integration
● Maximize JavaScript unit testing
SQL testing● Test stored procedure, constraints, functions and triggers
● Use your backend testing framework (like NUnit)● Easy to integrate in your Continuous Integration process
● Consider using a dedicated framework
● Or write your own
JavaScript testing● Use a JavaScript unit testing framework
● QUnit● jsTestDriver● Jasmine
Web Service testing● Use your backend testing framework (like NUnit)
● Use a JSON friendly version of WebClient:
● Input converted from .NET anonymous type to JSON
● Output converted from JSON to .NET dynamic type
● https://github.com/larsthorup/JsonClient
// whenvar votes = jsonClient.Get("Vote", "GetVotes", new { questionId = questionId });
// thenAssert.That(votes.Length, Is.EqualTo(1));var vote = votes[0];Assert.That(vote.ResponseText, Is.EqualTo("3"));Assert.That(vote.ParticipantName, Is.EqualTo("Lars Thorup"));
Legacy code● Add pinning tests
● special kinds of unit tests for legacy code
● verifies existing behaviour● acts as a safety net
● Can be driven by change requests
● Refactor the code to be able to write unit tests
● Add unit test for the change request
● Track coverage trend for existing code● and make sure it grows