What is Unit Testing?
Take the smallest reasonable piece of code, isolate it from the rest of the application, and programatically query it to ensure the results are what is expected.
- Bad Things about Unit Testing:
- Unit Tests are expensive to write and very expensive to maintain.
- You can test too much.
(Vertical axis is return on investment, horizontal is cost) - You can test the wrong things.
- You can easily develop a false sense of security when all your unit tests pass.
- Having 100% code coverage doesn't mean your application is error-free.
. - When all your unit tests pass, it doesn't mean your application is error-free.
- When all your unit tests pass, it doesn't mean your application is error-free. [sic]
- Why Do Unit Testing?
- Encourages better software design - less coupling, smaller, simpler methods
Instead of tightly interlocked code like this where to replace the "star" in the middle would take a lot of work:
Unit testing encourages code more like this, easy little blocks that can be replaced easily:
- Allows you to make deep changes in the software later with confidence
- Produces better quality software
- Gives you a framework for performance testing
- Finds errors in single components early, instead of finding multiple errors in multiple components which is exponentially more difficult.
- Easier to catch threading issues in unit tests
- The tests themselves are documentation
- It's more fun - really.
- Encourages better software design - less coupling, smaller, simpler methods
- The Process of Unit Testing
- Write the test before creating any logic in your target method
- The first run of the test should prove it fails
- Write the simplest code in the target method to pass the test
- Refactor as needed.
- Repeat
- Red, Green, Refactor
- Notes Unit Testing
- Don't confuse Unit Tests with integration tests or system tests.
- Unit tests should be independent of each other.
- Unit tests should not use external entities like a database. The test should use a mock instead. This requires external object access to be done through an interface, not a concrete class. This improves your design.
- Unit tests do not obviate the need for human testing of the system
- Unit tests allow you to throw exceptions easily in code. It's hard to simulate some network faults, but with a mock object it's easy.
- Many Unit test frameworks are available. We use NUnit.
- Unit Testing is critical to Agile software development.
- Where to put unit tests? In the object itself? In same assemble? In other assemble?
- Unit tests should be fast, less than a minute. To make them faster, move integration tests to separate suite, don't talk to the database, skip some on your local test box my using categories, and only run those on the build server, e.g., CruiseControl.Net.
- Each unit test should create it's own data, and delete it when it's finished. Integration tests should start with a clean database, add needed schema and data, then end with a clean database.
- When you find a bug, write a test that exposes that bug, and make sure it fails. Fix the bug, then run the test.
- Interesting Attributes in NUnit:
- [TestFixture]
- [Test]
- [ExpectedException]
- [Ignore]
- [Explicit]
- Our Kata exercise:
Build a case-insensitive ordered string set class. We will implement, Add(string), Count(), Contains(string), Remove(string), and GetEnumerator()
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using NUnit.Framework; namespace Utilities { // Case Insensitive Ordered String Set class public class CiosSet { public CiosSet() { } public void Add(string mystring) { } public int Count() { return 0; } } [TestFixture] public class CiosSetTest { [Test] public void Should_add_a_string_and_get_count_of_one() { Console.WriteLine(MethodBase.GetCurrentMethod()); //Arrange var set = new CiosSet(); //Act set.Add("abc"); //Assert Assert.IsTrue(set.Count() == 1); } } }
- A Few Other Types of Testing:
- Integration Tests
Do unit tested component work well together?
- System Tests
Do all the real components work well together, (no test double objects used)?
- Load Test
Does the system perform at or above expected levels of use?
- Endurance Test
Can the system perform at high levels of use for extended periods of time?
- Integration Tests