Transcript

A Better Way to SetupTest DataPoya Manouchehri

@PoyaManouchehripoya.manouchehri@readify.nethttp://askpoya.wordpress.com/

1. Intent is Unclear:• What is 160?• Is nationality important?

2. Test is Brittle:• Changing the constructor will break many tests

[Test]public void GivenAPersonUnder16_WhenAssigningADriverLicense_ThenAnExceptionIsThrown(){ // Arrange var sarah = new Person("Sarah", "Smith", DateTime.Today.AddYears(-15), 160, "Australian");

// Act / Assert Assert.Throws<InvalidOperationException>(() => sarah.AssignDriverLicense(DriverLicenseType.Automatic));}

public static partial class ObjectMother{ public static class Persons { public static Person FifteenYearOldSarah { get { return new Person("Sarah", "Smith", DateTime.Today.AddYears(-17), 160, "Australian"); } }

public static Person BrazilianRafael { get { return new Person("Rafael", "Gomez", new DateTime(1985, 12 , 2), 177, “Brazilian") } } }}

Object Mother Pattern

[Test]public void GivenAPersonUnder16_WhenAssigningADriverLicense_ThenAnExceptionIsThrown(){ var sarah = ObjectMother.Persons.FifteenYearOldSarah;

Assert.Throws<InvalidOperationException>(() => sarah.AssignDriverLicense(DriverLicenseType.Automatic));}

Object Mother Pattern

var person = new PersonBuilder() .WithName("Michael", "Jackson") .BornOn(new DateTime(1958, 8, 29)) .WithHeight(175) .WithNationality("American") .WhoHasDriverLicense(DriverLicenseType.Truck) .Build();

Builder Pattern

[Test]public void GivenAPersonUnder16_WhenAssigningADriverLicense_ThenAnExceptionIsThrown(){ var person = new PersonBuilder() .BornOn(DateTime.Today.AddYears(-15)) .Build();

Assert.Throws<InvalidOperationException>(() => person.AssignDriverLicense(DriverLicenseType.Automatic));}

Builder Pattern

[Test]public void GivenAPersonUnder16_WhenAssigningADriverLicense_ThenAnExceptionIsThrown(){ var person = ObjectMother.Persons.Sarah .BornOn(DateTime.Today.AddYears(-15)) .Build();

Assert.Throws<InvalidOperationException>(() => person.AssignDriverLicense(DriverLicenseType.Automatic));}

Builder Pattern

var people = PersonBuilder.CreateListOfSize(10) .All().Do(p => p.WithNationality("Australian")) .TheFirst(2).Do(p => p.BornOn(new DateTime(1980, 1, 1))) .TheNext(3).Do(p => p.BornOn(new DateTime(1974, 10, 15))) .Random(2).Do(p => p.WithHeight(180)) .BuildList<Person, PersonBuilder>();

Building Collections with NBuilder

var people = new[]{ new PersonBuilder().WithNationality("Australian").BornOn(new DateTime(1980, 1, 1)).Build(), new PersonBuilder().WithNationality("Australian").BornOn(new DateTime(1980, 1, 1)) .WithHeight(180).Build(), new PersonBuilder().WithNationality("Australian").BornOn(new DateTime(1974, 10, 15)).Build(), new PersonBuilder().WithNationality("Australian").BornOn(new DateTime(1974, 10, 15)) .WithHeight(180).Build(), new PersonBuilder().WithNationality("Australian").BornOn(new DateTime(1974, 10, 15)).Build()};

public class TransportCentre{ ...

public void PerformTruckDrivingTest(Person person) { // Do the test

person.AssignDriverLicense(DriverLicenseType.Truck); }}

Building Mocks with NSubstitute

[Test]public void GivenAPerson_WhenTruckDrivingTestIsCompleted_ThenThePersonIsAssignedALicense(){ var person = ObjectMother.Persons.John.Build(); var transportCentre = ObjectMother.TransportCentres.BrisbaneCityCentre.Build();

transportCentre.PerformTruckDrivingTest(person);

Assert.That(person.DriverLicenseType, Is.EqualTo(DriverLicenseType.Truck));}

What’s the actual “Unit” under test?• PerformTruckDrivingTest()• Not Person.AssignDriverLicense()

Building Mocks with NSubstitute

[Test]public void GivenAPerson_WhenTruckDrivingTestIsCompleted_ThenThePersonIsAssignedALicense(){ var person = ObjectMother.Persons.John.AsProxy().Build(); var transportCentre = ObjectMother.TransportCentres.BrisbaneCityCentre.Build();

transportCentre.PerformTruckDrivingTest(person);

person.Received().AssignDriverLicense(DriverLicenseType.Truck);}

Building Mocks with NSubstitute

What’s the actual “Unit” under test?• PerformTruckDrivingTest()• Not Person.AssignDriverLicense()

Building Mocks with NSubstitute

Caveats:• Requires a protected parameteress constructor• Requires the virtual keyword for any properties or methods that

need to be mocked

GitHub: https://github.com/robdmoore/NTestDataBuilder

Twitter: @robdmoore

NTestDataBuilder

Questions

top related