Test Driven Development (TDD) - Part Three

I am here to continue the thread related to Test Driven Development. Today, we will be going through some other aspects of TDD and more specifically, how to use the new features of Visual Studio to speed up the unit test case creation.

If you are new to TDD and haven’t gone through the previous parts of the series, I would recommend to go through those first, in order to understand the basics of it. Below are the links.

In previous articles, we have gone through traditional or manual ways of creating unit test cases. In this article, we will go through the new changes introduced starting from Visual Studio 2015 to create unit test cases faster and with good code coverage.

Currently, there are two ways available to generate unit test cases automatically as following.

Creating test case prototype by tools and logic manually

In this approach, the tool generates the prototype and developer needs to inject the logic to test the method. Below are the steps for the same.

For example, you have a class as following.
  1. public class Account : IAccount  
  2.     {  
  3.         double _balance;  
  4.   
  5.         public double CurrentBallance { get { return _balance; } }  
  6.           
  7.         public double Deposit(double amount)  
  8.         {  
  9.             if (amount > 0)  
  10.             {  
  11.                 _balance += amount;  
  12.                 return _balance;  
  13.             }  
  14.             else  
  15.             {  
  16.                 throw new ArgumentException("Invalid Deposit Amount!");  
  17.             }  
  18.                  }  
  19. public double Withdraw(double amount)  
  20.         {  
  21.             if (amount > 0 && _balance >= amount)  
  22.             {  
  23.                 _balance -= amount;  
  24.             }  
  25.             else  
  26.             {  
  27.                 throw new ArgumentException("No balance to Withdraw!");  
  28.             }  
  29.   
  30.             return _balance;  
  31.       }  
  32. }  
Right click on the class named "Account" and select “Create Unit Tests” (class should be public).

create
It will create Test Methods and put the dummy code with option selected in “Code for Test Method” in screen-shot of the above step. Since we have selected “Assert Failure”, it has put the same in the method body as following.
  1. [TestClass()]  
  2.     public class AccountTests  
  3.     {  
  4.         [TestMethod()]  
  5.         public void DepositTest()  
  6.         {  
  7.             Assert.Fail();  
  8.         }  
  9.   
  10.         [TestMethod()]  
  11.         public void WithdrawTest()  
  12.         {  
  13.             Assert.Fail();  
  14.         }  
  15.     }  
As you can see that this option has created the prototype of all the public methods, and now, the developer can use logic to test respective code blocks.
Creating both unit test cases and logic by tools

This is the fully or almost automated test case generation approach in which both, the prototype and the logic, are generated by the tools. It uses Pex framework for test case generation. There are two ways it can be used. Please note that this option is only available in Visual Studio 2015/2017 Enterprise Edition.

Create IntelliTest

This saves all the generated test cases into the files and then you can run them later.

create

A bit of manual effort is still required here to assert the generated methods. Below is the test class and methods generated by this option.

  1. /// <summary>This class contains parameterized unit tests for Account</summary>  
  2.     [PexClass(typeof(Account))]  
  3.     [PexAllowedExceptionFromTypeUnderTest(typeof(InvalidOperationException))]  
  4.     [PexAllowedExceptionFromTypeUnderTest(typeof(ArgumentException), AcceptExceptionSubtypes = true)]  
  5.     [TestClass]  
  6.     public partial class AccountTest  
  7.     {  
  8.         /// <summary>Test stub for get_CurrentBallance()</summary>  
  9.         [PexMethod]  
  10.         public double CurrentBallanceGetTest([PexAssumeUnderTest]Account target)  
  11.         {  
  12.             double result = target.CurrentBallance;  
  13.             return result;  
  14.             // TODO: add assertions to method AccountTest.CurrentBallanceGetTest(Account)  
  15.         }  
  16.   
  17.         /// <summary>Test stub for Deposit(Double)</summary>  
  18.         [PexMethod]  
  19.         public double DepositTest([PexAssumeUnderTest]Account target, double amount)  
  20.         {  
  21.             double result = target.Deposit(amount);  
  22.             return result;  
  23.             // TODO: add assertions to method AccountTest.DepositTest(Account, Double)  
  24.         }  
  25.   
  26.         /// <summary>Test stub for Withdraw(Double)</summary>  
  27.         [PexMethod]  
  28.         public double WithdrawTest([PexAssumeUnderTest]Account target, double amount)  
  29.         {  
  30.             double result = target.Withdraw(amount);  
  31.             return result;  
  32.             // TODO: add assertions to method AccountTest.WithdrawTest(Account, Double)  
  33.         }  
  34.   
  35.         [PexMethod]  
  36.         public double CurrentBallanceGet([PexAssumeUnderTest]Account target)  
  37.         {  
  38.             double result = target.CurrentBallance;  
  39.             return result;  
  40.             // TODO: add assertions to method AccountTest.CurrentBallanceGet(Account)  
  41.         }  
  42.     }  
Run IntelliTest:

This is the fully automated test case generation approach and it will let you run the test cases first. Then, you can select the appropriate ones to save. Upon saving, the new files get created for every public method and properties with the pattern of ClassName.TestMethodName.g.cs. The below screen short depicts the same.

run

In this option, test cases are complete in nature with all the Assert statement etc. Below are all the test cases generated for one of the methods (i.e. Deposit).

  1. public partial class AccountTest  
  2.     {  
  3.   
  4. [TestMethod]  
  5. [PexGeneratedBy(typeof(AccountTest))]  
  6. [ExpectedException(typeof(ArgumentException))]  
  7. public void DepositTestThrowsArgumentException836()  
  8. {  
  9.     Account account;  
  10.     double d;  
  11.     account = new Account();  
  12.     d = this.DepositTest(account, 0);  
  13. }  
  14.   
  15. [TestMethod]  
  16. [PexGeneratedBy(typeof(AccountTest))]  
  17. public void DepositTest716()  
  18. {  
  19.     Account account;  
  20.     double d;  
  21.     account = new Account();  
  22.     d = this.DepositTest(account, 1);  
  23.     Assert.AreEqual<double>(1, d);  
  24.     Assert.IsNotNull((object)account);  
  25.     Assert.AreEqual<double>(1, account.CurrentBallance);  
  26. }  
  27.     }  

Conclusion

In this article, we have gone through the tool based unit test creation approaches. We have started with "Create Unit Tests" option that creates test case prototype. We have also seen how to use IntelliTest option (Create/Run) to generate test cases automatically.

In the real world, you may use both manual and automated test cases in conjunction to have the good unit test code coverage. You can also download the attached demo project (TDD_Auto.zip) to go through the full source code referred in the article.

Hope you have liked the article. Look forward for your comments/suggestions.

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now