One of the drawbacks to unit testing has always been that you, as the developer, actually had to code the unit tests. In complex scenarios writing unit tests to exercise all the different permutations of logic can become very tedious and time consuming. I’ve often thought to myself, “Wouldn’t it be great if there was tooling built in to Visual Studio that could automatically generate a unit test suite?”. In Visual Studio 2015, with the introduction of IntelliTest (formally known as “Smart Unit Tests”), this is becoming closer to reality.
IntelliTest is the evolution of the Pex project from Microsoft Research. IntelliTest analysis every line of code in order to produce precise inputs that exercise every conditional branch. It then uses these inputs to generate a complete suite of unit tests with high code coverage. As the code base evolves IntelliTest can be rerun to not only add tests for newly added code but also to automatically keep existing tests in sync with any changes made.
The class below will be used to demonstrate IntelliTest in Visual Studio 2015. The class contains a single method, Add, that validates the supplied inputs, performs some conditional logic to determine the result and finally returns the result.
public class CalculatorService { public double Add(double x, double y) { //Input Validation if (x < 0) { throw new ArgumentOutOfRangeException(nameof(x), "Value must be greater than 0."); } if (y < 0) { throw new ArgumentOutOfRangeException(nameof(y), "Value must be greater than 0."); } double result; //Conditional Logic if (x.Equals(10)) { result = 0; } else if (y.Equals(10)) { if (x < 10) { result = 1; } else if (x < 100) { result = 2; } else { result = 3; } } else { result = (x + y); } //Return value return result; } }
To use IntelliTest to generate unit tests for the Add method right-click anywhere inside the method and choose Run IntelliTest from the context menu. If you wanted to generate unit tests for the entire class you could do so by right-clicking anywhere inside the class, but not inside a method, and then choosing Run IntelliTest from the context menu.
Once IntelliTest has finished analyzing the code the “IntelliTest Exploration Results” window is displayed.
The “IntelliTest Exploration Results” window contains all the permutations that the analysis of the code found would be required to fully exercise all conditional branches of the code. Each row in the results grid contains the values for the inputs and the expected result whether that be the expected value to be returned or the expected exception. Also, by clicking on one of the results in the grid the details of what the actual unit test looks like are displayed on the right hand side.
At this time IntelliTest has analyzed the code and displayed what the possible test scenarios are but it has not actually generated any unit tests yet. In order to tell IntelliTest to generate a test suite containing these tests you can either click the Save icon in the “IntelliTest Exploration Results” toolbar to generate a test for each result or you can select individual results in the grid, right click, and choose Save from the context menu.
When IntelliTest is finished generating the test suite a new Unit Test project, IntelliTest.Tests, is added to the solution along with a class, CalculatorServiceTest.cs, that contains the tests for the class under test. Using the “Text Explorer” window, which can be found in the “Test” -> “Windows” menu, the generated tests can be ran by clicking “Run All”.
As the code evolves, say new conditional logic is added as below, I can rerun IntelliTest as described above and another test will be included that exercises the newly added condition.
public class CalculatorService { public double Add(double x, double y) { //Input Validation if (x < 0) { throw new ArgumentOutOfRangeException(nameof(x), "Value must be greater than 0."); } if (y < 0) { throw new ArgumentOutOfRangeException(nameof(y), "Value must be greater than 0."); } double result; //Conditional Logic if (x.Equals(10)) { result = 0; } else if (y.Equals(10)) { if (x < 10) { result = 1; } else if (x < 100) { result = 2; } else if (x > 1000) { result = 3; } else { result = 4; } } else { result = (x + y); } //Return value return result; } }
The highlighted item below is the newly added test case that exercises the updated code.
IntelliTest can also help bring to light possible bugs as well. Another version of the Add method has been added to the CalculatorService. This version accepts an array of values and loops through it to calculate the sum.
public double Add(double[] vals) { double sum = 0; for (var i = 0; i < vals.Length; i++) { sum += vals[i]; } return sum; }
Running IntelliTest on the new version of Add shows an issue with the implementation.
The issue is that we have a failing scenario caused by passing null to the method. This issue can be easily addressed in the method by adding a check for null and reacting accordingly.
public double Add(double[] vals) { if (vals == null) { return 0; } double sum = 0; for (var i = 0; i < vals.Length; i++) { sum += vals[i]; } return sum; }
If you have been looking for a way to kick-start your unit testing IntelliTest in Visual Studio 2015 is a great place to start.
The post IntelliTest – Day 28 – Visual Studio 2015 appeared first on Falafel Software Blog.