Top Banner
Test-Driven Development and Unit Testing
40

Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

Dec 22, 2015

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

Test-Driven Development and Unit Testing

Page 2: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

2

Agenda

• Writing a Unit Test• Asserts• Test initialization and destruction• Generating Tests

Page 3: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

3

Why unit tests?

• To the extent possible, tests should run at all levels– unit (smallest granularity)– functional (public component contract)– integration (components in contact with each other)

• Time spend testing "in the small" will save time later– Agile practitioners write tests first– if not first, tests should at least be in small grain, in

conjunction with the code being tested

Page 4: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

4

Planning Unit Tests

public bool AgeCheck (DateTime dayOfBirth)

{ //Method returns true if the person is over age 18, otherwise false

……}

Page 5: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

5

Some Tests for AgeCheck• Check a date from exactly 18 years ago to the day.• Check a date from further back in time than 18 years.• Check a date from fewer than 18 years ago. • Check for a null value.• Check for the results based on the very minimum date

that can be entered.• Check for the results based on the very maximum date

that can be entered. • Check for an invalid date format being passed to the

method. • Check two year dates (such as 05/05/03) to determine

what century the two-year format is being treated as.• Check the time (if that matters) of the date.

Page 6: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

6

Unit Test Areas

• Boundary Values – min, max, etc.

• Equality – eg. 18

• Format – never trust data passed to your application.

• Culture – 11/26/07 vs. 26/11/07

• Exception Path

Page 7: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

7

Test-Driven Development

• Write the Unit Test

• Write enough code for the test to succeed

• Refactor the code to make it more efficient

Page 8: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

8

Why automate tests?

• Software without test cases must be assumed defective

• Manual test cases are unmaintainable– no guarantee that all tests will be run regularly, or at all– even if tests are run, process is tedious and error-prone

• Automated testing becomes part of the build process– easy to verify that changes do not break existing

functionality– encourages "build for now"– encourages refactoring

Page 9: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

9

Test Guidelines

• Keep application layered– Keep business logic separate from UI– Can then test business logic more easily

Page 10: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

10

Visual Studio Team Test

• Integrated into Visual Studio– Can generate tests from code– Setup/Teardown for all tests– Setup/teardown for individual tests– Methods are annotated to make them a test case

Page 11: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

11

Test Driven Development in Visual Studio1. Create a Project

2. Create a related Test Project

1. Write test case

2. Write 'real' code

3. Wash, rinse, repeat

Page 12: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

12

Writing a Test Class

• Test class contains test cases– Class and test identified by attributes

StarshipTest.cs

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DMTest{ [TestClass] public class StarshipTest { [TestMethod] public void TestWarpFactorLessThanMaxWarp() { } }}

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DMTest{ [TestClass] public class StarshipTest { [TestMethod] public void TestWarpFactorLessThanMaxWarp() { } }}

Page 13: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

13

Using Asserts

• Write tests by asserting truths– Use Assert class static methods– Several methods are available– Support different types through genericsStarshipTest.cs

[TestMethod]public void TestWarpFactorLessThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = 5;

Assert.AreEqual<Double>(ship.WarpFactor, 5);}

[TestMethod]public void TestWarpFactorLessThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = 5;

Assert.AreEqual<Double>(ship.WarpFactor, 5);}

Page 14: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

14

Assert class

• Static methodsAreEqual AreNotEqualAreNotSameAreSameFailInconclusiveIsFalse IsInstanceOfType IsNotInstanceOfTypeIsNotNull IsNullIsTrue

Page 15: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

15

CollectionAssert class

• Equivalent class for collections

AllItemsAreInstancesOfType

AllItemsAreNotNull

AllItemsAreUnique

AreEqualAreEquivalent (same elements)

AreNotEqual

AreNotEquivalent

Contains

DoesNotContain

IsNotSubsetOf

IsSubsetOf

Page 16: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

16

Write the code

• Write enough code so that the test passes

Starship.cs

namespace Space{ public class Starship { private double _warpFactor;

public double WarpFactor { get { return _warpFactor; } set { _warpFactor = value; } }

}}

namespace Space{ public class Starship { private double _warpFactor;

public double WarpFactor { get { return _warpFactor; } set { _warpFactor = value; } }

}}

Page 17: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

17

Running tests – Visual Studio

• Visual Studio Test Manager– Controls which tests to run ...– ... and when to run them– Allows for debugging of tests

Page 18: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

18

Managing Tests

• Test manger– Lets you specify the tests to run– Add lists of tests– Specify tests to run– Specify tests to debug– Data held in .vsmdi (test metadata) file

Page 19: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

19

Configuring Test Runs

• Specify data about test runs– Where to run; Enable code coverage; etc…

• Can have multiple configurations per project– Set active configuration in Test menu

Page 20: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

20

TestResults Directory

• Tests are not run from solution or project directory– Special TestResults directory is created– Contains subdirectory per test run– Subdirectory name is based on the timestamp of

current run– This contains a directory called 'Out'– Tests are run from 'Out' directory

Page 21: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

21

DeploymentItem Attribute

• Testing code and tested code may need access to files– Config files etc

• Use [DeploymentItem] to copy files to 'out' directory– Can specify a single file or a directory– Value can be relative or absoluteStarshipTest.cs

[TestMethod][DeploymentItem("SomeFile")]public void TestSomeThing(){}

[TestMethod][DeploymentItem("SomeFile")]public void TestSomeThing(){}

Page 22: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

22

Test Results

• Appear in pane

• Detailed results also available

Page 23: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

23

Testing Exceptions• Some code will throw exceptions

– Can test for expected and unexpected exceptionsStarshipTest.cs

[TestMethod]public void TestWarpFactorLessThanMaxWarp(){ try{ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP / 2; Assert.AreEqual<Double>(ship.WarpFactor, (Starship.MAX_WARP / 2)); } catch (Exception){ Assert.Fail("unexpected exception"); }}

[TestMethod]public void TestWarpFactorLessThanMaxWarp(){ try{ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP / 2; Assert.AreEqual<Double>(ship.WarpFactor, (Starship.MAX_WARP / 2)); } catch (Exception){ Assert.Fail("unexpected exception"); }}

StarshipTest.cs

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorGreaterThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP + 2;}

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorGreaterThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP + 2;}

Page 24: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

24

Write Next Test

• Write test

– write code to ensure test passes

StarshipTest.cs

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorGreaterThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = 11;}

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorGreaterThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = 11;}

Starship.cs

public double WarpFactor{ get { return _warpFactor; } set {if (value > MAX_WARP) throw new ArgumentException("Invalid warp"); _warpFactor = value; }}

public double WarpFactor{ get { return _warpFactor; } set {if (value > MAX_WARP) throw new ArgumentException("Invalid warp"); _warpFactor = value; }}

Page 25: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

25

Test Edge Conditions

• Edge conditions are where things notoriously go wrong– negative values– values at maximum or minimumStarshipTest.cs

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorNegative(){ Starship ship = new Starship(); ship.WarpFactor = -1;}

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorNegative(){ Starship ship = new Starship(); ship.WarpFactor = -1;}

StarshipTest.cs

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorEqualsMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP;}

[TestMethod][ExpectedException(typeof(ArgumentException))]public void TestWarpFactorEqualsMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP;}

Page 26: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

26

Inconclusive tests

• Sometimes want a test place holder– Can generate an 'inconclusive' assert– Used by VSTS when generating test codeStarshipTest.cs

[TestMethod]public void TestFirePhotonTorpedoe(){ Starship ship = new Starship(); Assert.Inconclusive("Code not specified");}

[TestMethod]public void TestFirePhotonTorpedoe(){ Starship ship = new Starship(); Assert.Inconclusive("Code not specified");}

Page 27: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

27

Ignoring Tests

• Used when you do not want the test to run

StarshipTest.cs

[TestMethod][Ignore]public void TestFirePhotonTorpedoe(){ Starship ship = new Starship(); Assert.Inconclusive("Code not specified");}

[TestMethod][Ignore]public void TestFirePhotonTorpedoe(){ Starship ship = new Starship(); Assert.Inconclusive("Code not specified");}

Page 28: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

28

Initializing Tests and Classes

• Common code can be refactored– Per class initialization– Per test initialization

• Use attributes

[ClassInitialize]

[ClassCleanup]

[TestInitialize]

[TestCleanup]

Page 29: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

29

StarshipTest.cs

[TestMethod]public void TestWarpFactorGreaterEqualsMaxWarp(){ ship.WarpFactor = Starship.MAX_WARP;}

[TestMethod]public void TestWarpFactorGreaterEqualsMaxWarp(){ ship.WarpFactor = Starship.MAX_WARP;}

Initialization example

• ClassInitialize used to perform one off initialization– Used if per test initialization is too expensive

• TestInitialization– Called per test– Better practice than ClassInitialize

Starship created here,and used here

StarshipTest.cs

private Starship ship = null;

[TestInitialize]public void TestSetup(){ // called for each test ship = new Starship();}

private Starship ship = null;

[TestInitialize]public void TestSetup(){ // called for each test ship = new Starship();}

Page 30: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

30

Testing existing code

• VSTS can generate tests– Not test driven development– However, can help with existing code

Page 31: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

31

What does Generated Code Look Like

• Attributes used to specify class/method behaviourStarshipTest.cs

[TestClass()]public class StarshipTest{ [ClassInitialize()] public void InitializeClass() {} [TestInitialize()] public void SetUp() {} [TestMethod()] public void CurrentWarpFactorTest() {} [TestCleanup()] public void InitializeClass() {} [ClassCleanup()] public void InitializeClass() {}}

[TestClass()]public class StarshipTest{ [ClassInitialize()] public void InitializeClass() {} [TestInitialize()] public void SetUp() {} [TestMethod()] public void CurrentWarpFactorTest() {} [TestCleanup()] public void InitializeClass() {} [ClassCleanup()] public void InitializeClass() {}}

Page 32: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

32

Testing Private Code

• Suppose you have a private method– Can’t call this directly from a test case– Need to use reflection to call method– VSTS will generate a stub to class manage this

code– Stub has an accessor

Starship.cs

public class Starship{ private void TorpedoesLeft(int numberToFire) { MaxTorpedoes -= numberToFire; }}

public class Starship{ private void TorpedoesLeft(int numberToFire) { MaxTorpedoes -= numberToFire; }}

Page 33: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

33

Calling the Private Method

• Test code calls the accessor method

StarshipTest.cs

[TestMethod()]public void TorpedoesLeftTest(){ Starship target = new Starship();

DMTest.Space_StarshipAccessor accessor = new DMTest.Space_StarshipAccessor(target);

int numberToFire = 0; // TODO: Initialize to an appropriate value accessor.TorpedoesLeft(numberToFire); ...}

[TestMethod()]public void TorpedoesLeftTest(){ Starship target = new Starship();

DMTest.Space_StarshipAccessor accessor = new DMTest.Space_StarshipAccessor(target);

int numberToFire = 0; // TODO: Initialize to an appropriate value accessor.TorpedoesLeft(numberToFire); ...}

Page 34: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

34

Generated Code for Private Test Case

• Access class uses reflectionStarship.cs

import Microsoft.VisualStudio.TestTools.UnitTesting;internal class BaseAccessor { protected PrivateObject m_privateObject; protected BaseAccessor(object target, PrivateType type) { m_privateObject = new PrivateObject(target, type); } ...}internal class Space_StarshipAccessor : BaseAccessor { protected static PrivateType m_privateType = new PrivateType(typeof(global::Space.Starship));

internal Space_StarshipAccessor(global::Space.Starship target) : base(target, m_privateType) { } internal void TorpedoesLeft(int numberToFire) { object[] args = new object[] {numberToFire}; m_privateObject.Invoke("TorpedoesLeft", new System.Type[] {typeof(int)}, args); }}

import Microsoft.VisualStudio.TestTools.UnitTesting;internal class BaseAccessor { protected PrivateObject m_privateObject; protected BaseAccessor(object target, PrivateType type) { m_privateObject = new PrivateObject(target, type); } ...}internal class Space_StarshipAccessor : BaseAccessor { protected static PrivateType m_privateType = new PrivateType(typeof(global::Space.Starship));

internal Space_StarshipAccessor(global::Space.Starship target) : base(target, m_privateType) { } internal void TorpedoesLeft(int numberToFire) { object[] args = new object[] {numberToFire}; m_privateObject.Invoke("TorpedoesLeft", new System.Type[] {typeof(int)}, args); }}

Page 35: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

35

What is Code Coverage

• Information about the code paths executed– Can be supplied automatically during unit tests– Can instrument code to provide data during other

tests

Page 36: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

36

Automatic code coverage

• Create or amend a run configuration

Page 37: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

37

Displaying Code Coverage

• Run tests– Display code coverage

Page 38: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

38

Code Coverage Results

• Code shown in different colours– May want to change the colours from the default

pastel shades

code nottouched

code partiallytouched

code touched

Page 39: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

39

Running test – command line

• VSTS comes with mstest command line utility– useful when using MSBuild to manage build process

Using MSTest

mstest /testcontainer:StartshipTest\bin\Debug\StartshipTest.dll

mstest /testmetadata:TDD.vsmdi

mstest /runconfig:localtestrun.testrunconfig /testmetadata:tdd.vsmdi

mstest /testcontainer:StartshipTest\bin\Debug\StartshipTest.dll

mstest /testmetadata:TDD.vsmdi

mstest /runconfig:localtestrun.testrunconfig /testmetadata:tdd.vsmdi

Page 40: Test-Driven Development and Unit Testing. 2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests.

40

Summary

• VSTS provides a good unit testing framework– Can define test class, test cases, etc.– Attribute driven

• Various ways of asserting truth of test• Various ways of initializing tests• Can generate tests from code• Can also test private methods