Test Driven Development

From bibbleWiki
Jump to navigation Jump to search

Introduction

Costs

The cost of software according to this course was Cost of software.png Tests are split into three categories

  • Does what was asked for
  • Responds appropriately to bad input
  • Acceptable Performance

Red Green Refactor

Start by writing tests with no code, write until test pass, refactor code. This is an iterative approach. Red green refactor.png

Benefits

  • Requirements are verified by the tests
  • Regression issues raised early
  • Costs of maintenance is lowered
  • Design first, when writing the tests first we are designing what they want
  • Reduces over engineering
  • Easy to know where you are in the project

Different Types of Testing

Types of Testing

  • Unit Testing
  • Functional Testing (UI/End-to-End)
  • Integrating Testing
  • User Acceptance Testing

Testing Approaches

  • Black Box testing (testing the interface)
  • White Box testing (testing internal aspects)

Tools

Some well known tools are

  • Selenium
  • Watir
  • VS Coded UI
  • Test Studio (Telerik)
  • Silk Test (Micro Focus)

Terminology

  • Test
  • Test Suite (Group of tests)
  • Before/After hooks to set up and tear down
  • Assert, eg. isTrue, isNull, areEqual
  • Test Execution
  • Test Runner (async/sync)

Example Fizz Buzz

Requirements

This has the following requirements Given a positive number

  • Divisible by 3 => "Fizz"
  • Divisible by 5 => "Buzz"
  • Divisible by 3 & 5 => "Fizz Buzz"
  • Otherwise => Number

Step 1

Create initial test

        private FizzBuzzService _fizzBuzzService;

        public FizzBuzzTests() {
            _fizzBuzzService = new FizzBuzzService();
        }

        [TestMethod]
        public void ShouldPrintNumber() {
            Assert.AreEqual("1", _fizzBuzzService.Print(1));
        }

Step 2

So lets build out tests up as we go until we get to.

...
        [TestMethod]
        public void ShouldPrintFizz()
        {
            Assert.AreEqual("Fizz", _fizzBuzz.Print(3));
        }

        [TestMethod]
        public void ShouldPrintBuzz()
        {
            Assert.AreEqual("Buzz", _fizzBuzz.Print(5));
            Assert.AreEqual("Buzz", _fizzBuzz.Print(10));
        }

        [TestMethod]
        public void ShouldPrintFizzBuzz()
        {
            Assert.AreEqual("FizzBuzz", _fizzBuzz.Print(15));
        }
...

Techniques

Dependency Injection

This is used in Frameworks all the time. Dependency injection.png The problem is that if we did not create a service and inject it but instead created in the class we are testing, it would be far harder to get the service to be in the correct state for the test. But if the service is passed in we could easily mock the state to do the test.

public class Greeter {
   private NameService nameService;
   public Greeter() {
     this.nameService = new NamService()
   }

   public string SayHello() {
      return $"Hello, {this.nameService.GetName()}";
   }
}

There are three types of dependency injection

  • Constructor injection (passed in the constructor and maybe an interface)
  • Property Injections (same as constructor but set on a property )
  • Interface Injection (abstract class or interface implementation)

In some languages functions a passed which, in their own right, are interfaces which support a given functionality. They are sometimes called Higher Order Functions.

Test Doubles

These are

Stubs

Test stubs are fake objects with pre-programmed behavior ( Simulation of behaviour from other units ), Most of times they are simply returning fixed values. They are typically used for one of two reasons:

Mocks

When most people talk about Mocks what they are actually referring to are Test Doubles. A Test Double is simply another object that conforms to the interface of the required Collaborator, and can be passed in its place. There are very few classes that operate entirely in isolation. Usually they need other classes or objects in order to function, whether injected via the constructor or passed in as method parameters. These are known as Collaborators or Depencies.

Fakes

Fake objects have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example). The simplest way to think of a Fake is as a step up from a Stub. This means not only does it return values, but it also works just as a real Collaborator would.

Spies

A test spy is an object that records its interaction with other objects throughout the code base. When deciding if a test was successful based on the state of available objects alone is not sufficient, we can use test spies and make assertions on things such as the number of calls, arguments passed to specific functions, return values and more.

Test spies are useful to test both callbacks and how certain functions/methods are used throughout the system under test. The following simplified example shows how to use spies to test how a function handles a callback:

Gotchas

Anti Patterns

  • Make sure execution of tests order does not matter
  • Make sure you are testing what and not how
  • Make sure test cycles are efficient, long tests might mean too coupled

Limitations

  • Does not eliminate defects
  • Need management support

Common Questions

  • Does not need agile but more benefits, aimed at lowering maintenance costs
  • Do not need to write tests first
  • No right way