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

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

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