29
TDD / BDD kickstart Lars Thorup ZeaLake Software Consulting March, 2016

Tddbdd workshop

Embed Size (px)

Citation preview

Page 1: Tddbdd workshop

TDD / BDD kickstart

Lars ThorupZeaLake Software Consulting

March, 2016

Page 2: Tddbdd workshop

Lars Thorup

● Software developer/architect● JavaScript, C#● Test Driven Development

● Coach● Agile engineering practices

and tools

● Founder● BestBrains● ZeaLake

● @larsthorup

Page 3: Tddbdd workshop

Basic unit testing● Assertions

● Fixtures

● Runners

● Debugging

● Exercise

Page 4: Tddbdd workshop

Assertions - Equality

Assert.AreEqual("San Francisco, California", location.ToDisplayString());

expect(location.toDisplayString()).toEqual('San Francisco, California');

assertThat(location.toDisplayString(), equalTo("San Francisco, California"));

Page 5: Tddbdd workshop

Assertions - Sameness● Microsoft

● Jasmine

● JUnit

Assert.AreSame(expectedLocation, actualLocation);

expect(actualLocation).toBe(expectedLocation);

assertThat(actualLocation, sameInstance(expectedLocation));

Page 6: Tddbdd workshop

Assertions - special values● Microsoft

● Jasmine

● JUnit

Assert.IsNull(location);Assert.IsTrue(location.IsValid());

expect(location).toBeNull();expect(location.isValid()).toBeTruthy();

assertThat(location, nullValue());

Page 7: Tddbdd workshop

Assertions - floating point● Microsoft

● Jasmine

● JUnit

Assert.AreEqual(10.23, location.Diameter(), 0.01);

expect(location.diameter()).toBeCloseTo(10.23, 2);

assertThat(location.diameter(), closeTo(10.23, 0.01));

Page 8: Tddbdd workshop

Assertions - collections● Microsoft

● Jasmine

● JUnit

CollectionAssert.AreEquivalent(new [] {"a", "b"}, names);

expect(names).toEqual(['a', 'b']);

assertThat(names, containsInAnyOrder("a", "b"))

Page 9: Tddbdd workshop

Assertions - exceptions● Microsoft

● Jasmine

● JUnit

[ExpectedException(typeof(ArgumentException))]public void SomeTest () { DivideBy(0); }

expect(function () { divideBy(0); }).toThrow(ArgumentError);

@Test(expected = ArgumentException.class) public void someTest() { divideBy(0); }

Page 10: Tddbdd workshop

Assertions - exceptions● Microsoft

try{ DivideBy(0); Assert.Fail();}catch(ArgumentException ex){ Assert.AreEqual("Cannot divide by 0", ex.Message);}

Page 11: Tddbdd workshop

Fixtures● Microsoft

● Jasmine

● JUnit

[TestInitialize] public void Initialize() { ... }

[TestCleanup] public void Cleanup() { ... }

beforeEach(function () { ... });

afterEach(function () { ... });

@Before public void before() { ... }

@After public void after() { ... }

Page 12: Tddbdd workshop

Runners● Microsoft

● Jasmine

● JUnit

Visual Studio

Test | Run | All Tests

Grunt & Karma

npm test

IntelliJ IDEA

Run | Configurations | JUnit

Page 13: Tddbdd workshop

Debugging● Microsoft

● Jasmine

● JUnit

Visual Studio

Test | Debug | All Tests

Grunt & Karma & Chrome

npm run test:chrome

IntelliJ IDEA

Debug | Configurations | JUnit

Page 14: Tddbdd workshop

Exercise - unit testing● Try out assertions

● Equality and sameness● Floating point● Collections● Exceptions

● Verify that fixture methods run for every test

● Debug a test

Page 15: Tddbdd workshop

Workflow of TDD / BDD

Failingtest

Succeedingtest

Gooddesign Refactor

Code

Test

BehaviorThink, talk

Page 16: Tddbdd workshop

TDD Demo - IP# -> Location display● Convert this JSON ● To a display string

San Francisco, California

Farum, Denmark

Philippines

{ CountryCode = "US", Country = "USA", Region = "California", City = "San Francisco" }

{ CountryCode = "DK", Country = "Denmark", Region = "Hovedstaden", City = "Farum" }

{ CountryCode = "PH", Country = "Philippines", Region = "", City = "" }

Page 17: Tddbdd workshop

Why TDD?

Well-designedJust Works™

Refactorable

No debugger

Documented

Productive!

Page 18: Tddbdd workshop

Exercise - TDD● Input: a date

● Output: the time since that date, human readable● "2 minutes ago"● "1week ago"● "3 months ago"

● Think first about the precise behavior

● Then write one test for the simplest case not covered yet● and make it fail for the right reason

● Then write the code to make the test pass

● ...repeat :)

Page 19: Tddbdd workshop

● 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

● Way more fun :)

Why is TDD a good thing?

Page 20: Tddbdd workshop

Questions!

Page 21: Tddbdd workshop

TDD - questions● How fine grained is the TDD cycle?

● Do we always write tests before code?

● Why can't we just write the tests afterwards?

● How many tests are enough?

● How do we test private methods?

● Do we have to setup all dependencies?

● What about our legacy code?

Page 22: Tddbdd workshop

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();}

Page 23: Tddbdd workshop

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

Page 24: Tddbdd workshop

Stubs● Hand crafted

● More effort to write

● Easier to maintain

● Can be more "black box" than mocks

Page 25: Tddbdd workshop

Mocks● Mocks are automatically generated stubs

● Easy to use

● More "magical", may be slower

● More effort to maintain

● Will be more "white-box" than stubs

● Example frameworks:● C#: NSubstitute● JavaScript: Sinon.JS● Java: Easymock / Powermock

Page 26: Tddbdd workshop

Stubs - example

public 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));}

Page 27: Tddbdd workshop

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();}

Page 28: Tddbdd workshop

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}));}

Page 29: Tddbdd workshop

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