Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Best practices for writing first class unit tests

3,050 views

Published on

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.

Published in: Technology, Business
  • Be the first to comment

Best practices for writing first class unit tests

  1. 1. Writing First Class Unit Tests<br />Best Practices<br />Dennis Doomen | Principal Consultant | Aviva Solutions<br />@ddoomen | www.dennisdoomen.net<br />
  2. 2. Unit tests…<br />Are fast<br />Are automated<br />Are small<br />Run in-memory<br />Unit = Single class…or…<br />Dennis Doomen<br />
  3. 3. Integration tests…<br />Can cross boundaries<br />Are slower<br />Candepend on external resources<br />Dennis Doomen<br />
  4. 4. Otherforms of testing<br />System Testing<br />UsabilityTesting<br />User AcceptanceTesting<br />ATDD<br />Dennis Doomen<br />
  5. 5. My First Attempt…<br />Dennis Doomen<br />
  6. 6.         [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());        }<br />IntentionRevealing<br />Small andfocused<br />Clearcauseand effect<br />Test onecondition<br />Independent<br />Repeatable<br />No side-effects<br />
  7. 7. If it is not important for the test, it is very important not to show it…<br />My first attempt…<br />
  8. 8. A small improvement…<br />  [TestMethod]        public void When_searching_it_should_account_for_the_city_and_minimal_account_balance()        {<br /> }<br />My first attempt…<br />
  9. 9. My first attempt…<br />More improvements…<br />Isolate The Ugly Stuff<br />UseDependencyInjection<br />
  10. 10.        [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);        }<br />
  11. 11.     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;        }    }<br />
  12. 12. My first attempt…<br />Some more intentions…<br />
  13. 13.        [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);<br />       [TestMethod]        public void When_searching_it_should_account_for_the_city_and_minimal_account_balance()        { <br />           var someCustomer = new CustomerBuilder().Build();            var someOtherCustomer = new CustomerBuilder().Build();            var theServiceAgent = new ServiceAgentStub();            theServiceAgent.AddDummyCustomers(someCustomer, someOtherCustomer);<br />
  14. 14.     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;        }    }<br />
  15. 15. My first attempt…<br />Arrange…act…assert…<br />
  16. 16. My first attempt…<br />State versus interaction<br />
  17. 17. My first attempt…<br />Rules toease unit testing…<br />Test small beforeyou test big<br />Prefer state-basedtesting<br />Keep out of the debugger hell<br />
  18. 18.             //-------------------------------------------------------------------------------------------------------------------            // 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);<br />            //-------------------------------------------------------------------------------------------------------------------            // Assert            //-------------------------------------------------------------------------------------------------------------------            viewModel.Customers.Should().Equal(someCustomers);            theServiceAgent.City.Should().Be("Washington");            theServiceAgent.MinimumAccountBalance.Should().Be(10000m);            changedPropertyName.Should().Be("Customers");<br />
  19. 19. My first attempt…<br />AAA versus BDD-style<br />
  20. 20. Andnowforsomefaking…<br />
  21. 21. Karl Seguin wrote:<br />Refusing<br />Getting too excited<br />Testing everything!<br />Integration testing<br />Discover mocking<br />Mocking everything<br />Becoming effective<br />28 May 2009<br />Dennis Doomen<br />
  22. 22. Background InformationJeremy’sLaws of TDD, Test Data Builder, Applying Domain Driven Design (Jimmy Nillson), xUnitPatterns<br />Example Code, FrameworksSilverlightCookbook, Fake It Easy, FluentAssertions<br />
  23. 23. Emaildennis.doomen@avivasolutions.nl<br />Twitter<br />@ddoomen<br />Blogwww.dennisdoomen.net<br />

×