best practices for writing first class unit tests

23
© 2011 Aviva Solutions 20-06-22 Writing First Class Unit Tests Best Practices Dennis Doomen | Principal Consultant | Aviva Solutions @ddoomen | www.dennisdoomen.net

Upload: dennis-doomen

Post on 14-May-2015

2.405 views

Category:

Technology


2 download

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

Page 1: 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

Page 2: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

Unit tests…Are fast

Are automatedAre small

Run in-memory

Unit = Single class…or…

Dennis Doomen

Page 3: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

Integration tests…Can cross boundaries

Are slowerCan depend on external

resources

Dennis Doomen

Page 4: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

Other forms of testingSystem Testing

Usability TestingUser Acceptance Testing

ATDD

Dennis Doomen

Page 5: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

My First Attempt…

Dennis Doomen

Page 6: Best practices for writing first class unit tests

© 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

Page 7: Best practices for writing first class unit tests

© 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…

Page 8: Best practices for writing first class unit tests

© 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…

Page 9: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023My first attempt…

More improvements…Isolate The Ugly Stuff

Use Dependency Injection

Page 10: Best practices for writing first class unit tests

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

Page 11: Best practices for writing first class unit tests

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

Page 12: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023My first attempt…

Some more intentions…

Page 13: Best practices for writing first class unit tests

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

Page 14: Best practices for writing first class unit tests

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

Page 15: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023My first attempt…

Arrange…act…assert…

Page 16: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023My first attempt…

State versus interaction

Page 17: Best practices for writing first class unit tests

© 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

Page 18: Best practices for writing first class unit tests

© 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");

Page 19: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023My first attempt…

AAA versus BDD-style

Page 20: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

And now for some faking…

Page 21: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

Karl Seguin wrote:Refusing

Getting too excitedTesting everything!Integration testingDiscover mocking

Mocking everythingBecoming effective

28 May 2009Dennis Doomen

Page 23: Best practices for writing first class unit tests

© 2011 Aviva Solutions 12 april 2023

[email protected]

Twitter@ddoomen

Blogwww.dennisdoomen.net