Best practices for writing first class unit tests

Post on 14-May-2015

2.402 views 2 download

Tags:

description

A step by step demonstration of the practices and principles you can use to improve the quality and maintainability of your automated unit tests in .NET.

Transcript of Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

Writing First Class Unit Tests

Best Practices

Dennis Doomen | Principal Consultant | Aviva Solutions@ddoomen | www.dennisdoomen.net

© 2011 Aviva Solutions 12 april 2023

Unit tests…Are fast

Are automatedAre small

Run in-memory

Unit = Single class…or…

Dennis Doomen

© 2011 Aviva Solutions 12 april 2023

Integration tests…Can cross boundaries

Are slowerCan depend on external

resources

Dennis Doomen

© 2011 Aviva Solutions 12 april 2023

Other forms of testingSystem Testing

Usability TestingUser Acceptance Testing

ATDD

Dennis Doomen

© 2011 Aviva Solutions 12 april 2023

My First Attempt…

Dennis Doomen

© 2011 Aviva Solutions 12 april 2023

[TestMethod] public void FindCustomersTest1() { var viewModel = new CustomerManagementViewModel(); string propertyChanged = ""; viewModel.PropertyChanged += (sender, args) => propertyChanged = args.PropertyName; viewModel.City = "Washington"; viewModel.MinimumAccountBalance = 10000;

viewModel.Find();

var customers = viewModel.Customers; Assert.AreEqual(2, customers.Count()); Assert.IsTrue(customers.Any(c => c.Id == 15)); Assert.IsTrue(customers.Any(c => c.Id == 81)); Assert.AreEqual("Customers", propertyChanged);

viewModel.City = ""; viewModel.MinimumAccountBalance = 0;

propertyChanged = "";

viewModel.Find();

Assert.AreEqual(102, viewModel.Customers.Count()); }

Intention RevealingSmall and focused

Clear cause and effectTest one condition

IndependentRepeatable

No side-effects

© 2011 Aviva Solutions 12 april 2023

If it is not important for the

test, it is very important not to

show it…

My first attempt…

© 2011 Aviva Solutions 12 april 2023

A small improvement… [TestMethod]

public void When_searching_it_should_account_for_the_city_and_minimal_account_balance() { }

My first attempt…

© 2011 Aviva Solutions 12 april 2023My first attempt…

More improvements…Isolate The Ugly Stuff

Use Dependency Injection

© 2011 Aviva Solutions 12 april 2023

[TestMethod] public void When_searching_it_should_account_for_the_city_and_minimal_account_balance() { var dummyCustomers = new[] { new Customer(), new Customer()};

var service = new ServiceAgentStub(); service.AddDummyCustomers(dummyCustomers);

var viewModel = new CustomerManagementViewModel(service) { City = "Washington", MinimumAccountBalance = 10000 };

string propertyChanged = null; viewModel.PropertyChanged += (sender, args) => propertyChanged = args.PropertyName;

viewModel.Find();

Assert.AreEqual(2, viewModel.Customers.Count()); CollectionAssert.AreEquivalent(viewModel.Customers.ToArray(), dummyCustomers);

Assert.AreEqual("Washington", service.City); Assert.AreEqual(10000, service.MinimumAccountBalance);

Assert.AreEqual("Customers", propertyChanged); }

© 2011 Aviva Solutions 12 april 2023

public class ServiceAgentStub : IServiceAgent { private List<Customer> dummyCustomers = new List<Customer>();

public decimal? MinimumAccountBalance { get; set; } public string City { get; set; }

public void AddDummyCustomers(params Customer[] customers) { dummyCustomers.AddRange(customers); }

public IEnumerable<Customer> FindCustomers(string city, decimal? minimumAccountBalance) { City = city; MinimumAccountBalance = minimumAccountBalance;

return dummyCustomers; } }

© 2011 Aviva Solutions 12 april 2023My first attempt…

Some more intentions…

© 2011 Aviva Solutions 12 april 2023

[TestMethod] public void When_searching_it_should_account_for_the_city_and_minimal_account_balance() { var dummyCustomers = new[] { new Customer(), new Customer()};

var service = new ServiceAgentStub(); service.AddDummyCustomers(customer1, customer2);

[TestMethod] public void When_searching_it_should_account_for_the_city_and_minimal_account_balance() { var someCustomer = new CustomerBuilder().Build(); var someOtherCustomer = new CustomerBuilder().Build();

var theServiceAgent = new ServiceAgentStub(); theServiceAgent.AddDummyCustomers(someCustomer, someOtherCustomer);

© 2011 Aviva Solutions 12 april 2023

public class CustomerBuilder : TestDataBuilder<Customer> { private static long nextId = 1; private string city = "Redmond"; private decimal accountBalance = 100;

protected override Customer OnBuild() { return new Customer { Id = nextId++, City = city, AccountBalance = accountBalance }; }

public CustomerBuilder InCity(string city) { this.city = city; return this; }

public CustomerBuilder WithAccountBalance(decimal accountBalance) { this.accountBalance = accountBalance; return this; } }

© 2011 Aviva Solutions 12 april 2023My first attempt…

Arrange…act…assert…

© 2011 Aviva Solutions 12 april 2023My first attempt…

State versus interaction

© 2011 Aviva Solutions 12 april 2023My first attempt…

Rules to ease unit testing…Test small before you test big

Prefer state-based testingKeep out of the debugger hell

© 2011 Aviva Solutions 12 april 2023

//------------------------------------------------------------------------------------------------------------------- // Assert //------------------------------------------------------------------------------------------------------------------- Assert.AreEqual(2, viewModel.Customers.Count()); var expectedCustomers = new[] { someCustomer, someOtherCustomer}; CollectionAssert.AreEquivalent(viewModel.Customers.ToArray(), expectedCustomers);

Assert.AreEqual("Washington", theServiceAgent.City); Assert.AreEqual(10000, theServiceAgent.MinimumAccountBalance);

Assert.AreEqual("Customers", changedPropertyName);

//------------------------------------------------------------------------------------------------------------------- // Assert //------------------------------------------------------------------------------------------------------------------- viewModel.Customers.Should().Equal(someCustomers);

theServiceAgent.City.Should().Be("Washington"); theServiceAgent.MinimumAccountBalance.Should().Be(10000m);

changedPropertyName.Should().Be("Customers");

© 2011 Aviva Solutions 12 april 2023My first attempt…

AAA versus BDD-style

© 2011 Aviva Solutions 12 april 2023

And now for some faking…

© 2011 Aviva Solutions 12 april 2023

Karl Seguin wrote:Refusing

Getting too excitedTesting everything!Integration testingDiscover mocking

Mocking everythingBecoming effective

28 May 2009Dennis Doomen

© 2011 Aviva Solutions 12 april 2023

Emaildennis.doomen@avivasolutions.nl

Twitter@ddoomen

Blogwww.dennisdoomen.net