Download presentation
Presentation is loading. Please wait.
1
Test-Driven Development and Unit Testing
2
2 Agenda Writing a Unit Test Asserts Test initialization and destruction Generating Tests
3
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
4
4 Planning Unit Tests public bool AgeCheck (DateTime dayOfBirth) { //Method returns true if the person is over age 18, otherwise false …… }
5
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.
6
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
7
7 Test-Driven Development Write the Unit Test Write enough code for the test to succeed Refactor the code to make it more efficient
8
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
9
9 Test Guidelines Keep application layered –Keep business logic separate from UI –Can then test business logic more easily
10
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
11
11 Test Driven Development in Visual Studio 1.Create a Project 2.Create a related Test Project 1.Write test case 2.Write 'real' code 3.Wash, rinse, repeat
12
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() { }
13
13 Using Asserts Write tests by asserting truths –Use Assert class static methods –Several methods are available –Support different types through generics StarshipTest.cs [TestMethod] public void TestWarpFactorLessThanMaxWarp() { Starship ship = new Starship(); ship.WarpFactor = 5; Assert.AreEqual (ship.WarpFactor, 5); } [TestMethod] public void TestWarpFactorLessThanMaxWarp() { Starship ship = new Starship(); ship.WarpFactor = 5; Assert.AreEqual (ship.WarpFactor, 5); }
14
14 Assert class Static methods AreEqual AreNotEqual AreNotSame AreSame Fail Inconclusive IsFalse IsInstanceOfType IsNotInstanceOfType IsNotNull IsNull IsTrue
15
15 CollectionAssert class Equivalent class for collections AllItemsAreInstancesOfType AllItemsAreNotNull AllItemsAreUnique AreEqual AreEquivalent (same elements) AreNotEqual AreNotEquivalent Contains DoesNotContain IsNotSubsetOf IsSubsetOf
16
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; }
17
17 Running tests – Visual Studio Visual Studio Test Manager –Controls which tests to run... –... and when to run them –Allows for debugging of tests
18
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
19
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
20
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
21
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 absolute StarshipTest.cs [TestMethod] [DeploymentItem("SomeFile")] public void TestSomeThing() { } [TestMethod] [DeploymentItem("SomeFile")] public void TestSomeThing() { }
22
22 Test Results Appear in pane Detailed results also available
23
23 Testing Exceptions Some code will throw exceptions –Can test for expected and unexpected exceptions StarshipTest.cs [TestMethod] public void TestWarpFactorLessThanMaxWarp(){ try{ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP / 2; Assert.AreEqual (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 (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; }
24
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; }
25
25 Test Edge Conditions Edge conditions are where things notoriously go wrong –negative values –values at maximum or minimum StarshipTest.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; }
26
26 Inconclusive tests Sometimes want a test place holder –Can generate an 'inconclusive' assert –Used by VSTS when generating test code StarshipTest.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"); }
27
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"); }
28
28 Initializing Tests and Classes Common code can be refactored –Per class initialization –Per test initialization Use attributes [ClassInitialize] [ClassCleanup] [TestInitialize] [TestCleanup]
29
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(); }
30
30 Testing existing code VSTS can generate tests –Not test driven development –However, can help with existing code
31
31 What does Generated Code Look Like Attributes used to specify class/method behaviour StarshipTest.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() {} }
32
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; }
33
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);... }
34
34 Generated Code for Private Test Case Access class uses reflection Starship.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); }
35
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
36
36 Automatic code coverage Create or amend a run configuration
37
37 Displaying Code Coverage Run tests –Display code coverage
38
38 Code Coverage Results Code shown in different colours –May want to change the colours from the default pastel shades code not touched code partially touched code touched
39
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
40
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
Similar presentations
© 2024 SlidePlayer.com Inc.
All rights reserved.