1 / 40

Test-Driven Development and Unit Testing

Test-Driven Development and Unit Testing. Agenda. Writing a Unit Test Asserts Test initialization and destruction Generating Tests. Why unit tests?. To the extent possible, tests should run at all levels unit (smallest granularity) functional (public component contract)

lynton
Download Presentation

Test-Driven Development and Unit Testing

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  4. Planning Unit Tests public bool AgeCheck (DateTime dayOfBirth) { //Method returns true if the person is over age 18, otherwise false …… }

  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. 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. Test-Driven Development • Write the Unit Test • Write enough code for the test to succeed • Refactor the code to make it more efficient

  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. Test Guidelines • Keep application layered • Keep business logic separate from UI • Can then test business logic more easily

  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. Test Driven Development in Visual Studio • Create a Project • Create a related Test Project • Write test case • Write 'real' code • Wash, rinse, repeat

  12. StarshipTest.cs using Microsoft.VisualStudio.TestTools.UnitTesting; namespaceDMTest { [TestClass] publicclass StarshipTest { [TestMethod] publicvoid TestWarpFactorLessThanMaxWarp() { } } } Writing a Test Class • Test class contains test cases • Class and test identified by attributes

  13. StarshipTest.cs [TestMethod] publicvoid TestWarpFactorLessThanMaxWarp() { Starship ship = new Starship(); ship.WarpFactor = 5; Assert.AreEqual<Double>(ship.WarpFactor, 5); } Using Asserts • Write tests by asserting truths • Use Assert class static methods • Several methods are available • Support different types through generics

  14. Assert class • Static methods • AreEqual • AreNotEqual • AreNotSame • AreSame • Fail • Inconclusive • IsFalse • IsInstanceOfType • IsNotInstanceOfType • IsNotNull • IsNull • IsTrue

  15. CollectionAssert class • Equivalent class for collections • AllItemsAreInstancesOfType • AllItemsAreNotNull • AllItemsAreUnique • AreEqual • AreEquivalent (same elements) • AreNotEqual • AreNotEquivalent • Contains • DoesNotContain • IsNotSubsetOf • IsSubsetOf

  16. Starship.cs namespace Space { publicclass Starship { privatedouble _warpFactor; publicdouble WarpFactor { get { return _warpFactor; } set { _warpFactor = value; } } } } Write the code • Write enough code so that the test passes

  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. 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. 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. 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. StarshipTest.cs [TestMethod] [DeploymentItem("SomeFile")] publicvoid TestSomeThing() { } 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

  22. Test Results • Appear in pane • Detailed results also available

  23. StarshipTest.cs StarshipTest.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] [ExpectedException(typeof(ArgumentException))] public void TestWarpFactorGreaterThanMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP + 2; } Testing Exceptions • Some code will throw exceptions • Can test for expected and unexpected exceptions

  24. Starship.cs StarshipTest.cs [TestMethod] [ExpectedException(typeof(ArgumentException))] public void TestWarpFactorGreaterThanMaxWarp() { Starship ship = new Starship(); ship.WarpFactor = 11; } public double WarpFactor{ get { return _warpFactor; } set {if (value > MAX_WARP) thrownew ArgumentException("Invalid warp"); _warpFactor = value; } } Write Next Test • Write test • write code to ensure test passes

  25. StarshipTest.cs StarshipTest.cs [TestMethod] [ExpectedException(typeof(ArgumentException))] public void TestWarpFactorNegative(){ Starship ship = new Starship(); ship.WarpFactor = -1; } [TestMethod] [ExpectedException(typeof(ArgumentException))] publicvoid TestWarpFactorEqualsMaxWarp(){ Starship ship = new Starship(); ship.WarpFactor = Starship.MAX_WARP; } Test Edge Conditions • Edge conditions are where things notoriously go wrong • negative values • values at maximum or minimum

  26. StarshipTest.cs [TestMethod] public void TestFirePhotonTorpedoe() { Starship ship = new Starship(); Assert.Inconclusive("Code not specified"); } Inconclusive tests • Sometimes want a test place holder • Can generate an 'inconclusive' assert • Used by VSTS when generating test code

  27. StarshipTest.cs [TestMethod] [Ignore] public void TestFirePhotonTorpedoe() { Starship ship = new Starship(); Assert.Inconclusive("Code not specified"); } Ignoring Tests • Used when you do not want the test to run

  28. Initializing Tests and Classes • Common code can be refactored • Per class initialization • Per test initialization • Use attributes • [ClassInitialize] • [ClassCleanup] • [TestInitialize] • [TestCleanup]

  29. StarshipTest.cs StarshipTest.cs [TestMethod] public void TestWarpFactorGreaterEqualsMaxWarp(){ ship.WarpFactor = Starship.MAX_WARP; } private Starship ship = null; [TestInitialize] public void TestSetup(){ // called for each test ship = new Starship(); } 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

  30. Testing existing code • VSTS can generate tests • Not test driven development • However, can help with existing code

  31. 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() {} } What does Generated Code Look Like • Attributes used to specify class/method behaviour

  32. Starship.cs public class Starship { private void TorpedoesLeft(int numberToFire) { MaxTorpedoes -= numberToFire; } } 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

  33. 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); ... } Calling the Private Method • Test code calls the accessor method

  34. 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 staticPrivateType 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); } } Generated Code for Private Test Case • Access class uses reflection

  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. Automatic code coverage • Create or amend a run configuration

  37. Displaying Code Coverage • Run tests • Display code coverage

  38. code touched code not touched code partially touched Code Coverage Results • Code shown in different colours • May want to change the colours from the default pastel shades

  39. Using MSTest mstest /testcontainer:StartshipTest\bin\Debug\StartshipTest.dll mstest /testmetadata:TDD.vsmdi mstest /runconfig:localtestrun.testrunconfig /testmetadata:tdd.vsmdi Running test – command line • VSTS comes with mstest command line utility • useful when using MSBuild to manage build process

  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

More Related