Posts tagged ‘Code Coverage’

What’s in a name?

Google “Pex” and what do you expect to find? According to Wikipedia:

“Cross-linked polyethylene, commonly abbreviated PEX or XLPE, is a form of polyethylene with cross-links.”

That’s not really the definition I was hoping for, so if we qualify the search with “Microsoft” then we have a better chance of encountering a more meaningful software related defintion:

“Pex (Program EXploration) produces a traditional unit test suite with high code coverage. A parameterized unit test is simply a method that takes parameters, calls the code under test, and states assertions. Given a parameterized unit test written in a .NET language, Pex automatically produces a small unit test suite with high code and assertion coverage. To do so, Pex performs a systematic white box program analysis.”

Well, now we have got that cleared up, let’s have a look at what we can do with it.

For a start, the most powerful aspect of Pex is the white box analysis that it performs on an existing unit of code. This analysis will create the appropriate boundary test (as best as it can). The generation of test code is not extensive; it covers the simple cases and where complex data type are used, the developer will most likely still have to fall back on utilizing test methods such as mocking.

Part of the secret of getting Pex to work your code properly is to provide some form of Parameterized Unit Test to get it started. Another important factor is to use the Stubs Lightweight Framework (also a research project from Microsoft) to create appropriate stubs for the Parameterized Unit Tests. Both methods can be generated quite easily; however a few tweaks are required to get it just right.

In order to illustrate the work involved we shall revisit the previous example of one of the Managers (either EmployeeManager or CustomerManager). The following walkthroughs assume that you have Pex installed and that you have either Visual Studio Test edition or Team edition installed.

How it is done using defaults…

In the first stage we need to instruct the assembly being tested that it will give the new unit test assembly access to the internal methods and properties (see my previous post):

  1. [assembly: InternalsVisibleTo("DiDemo.Managers.Tests")]

The second stage is to create the Unit Test project that will “house” the Pex generated unit Tests. The easiest way to do this is to use the Pex auto-generation functionality from the Code Context menu:

  1. Right click the class name that will have the unit tests generated (in our case CustomerManager) and select the Pex->Create Parameterized Unit Test Stubs:

    Generate Test Stubs
  2. Configure the Unit Test project that you want to target the auto-generated code to (in this case we will select from the drop down combo box) and click Ok when you are ready:

    Test Stubs Dialog
  3. Specify (if you wish) the location of the new Test project and click OK:

    Test Stubs Dialog
  4. The result will be the generation of the new Unit Test project complete with auto-generated Stubs and Parameterized Unit Test Stubs:

    Generated Unit Tests

Having a quick look at what is generated, we see that the stubx file is the configuration xml for the Microsoft Stubs engine that indicates which assembly we are generating the stubs from – thereby creating the stubs designer file:

Generated Stubs

As we will see later on, the stubs that are generated in this “default” manner might not be exactly what we want, although the concept is going to come in handy.

Next is the auto generated parameterized unit test stubs. These methods are intended to be used by Pex to generate the unit test methods when the analysis is run. In their current format they do little but to serve as place holders:

Generated Test Methods

At this stage, as an educational exercise, we can run the Pex Explorations to see what gets thrown up:

Run Pex Explorations

Looking through the results there is one glaring issue and that is the fact that the way we have created the architecture of the managers (using Dependency Injection and having default behavior); we have inadvertently generated integration tests (the clue is in the fact that we are seeing SqlExceptions – this means that we have also explored the underlying data access layer, which we had tried so hard to inject the dependency for:

First Results Pex Explorations

The thought through way…

In my mind we don’t want to generate stubs of the classes that we are actually testing, we want to generate stubs of the interfaces that we are injecting into the classes we are testing – a subtle difference, but nevertheless one that is important.

Therefore, strip out the constructor tests (parameter-less and single DI parametered constructor):

Delete Constructor Tests

After those test methods are removed, delete the Manager stubs (we aren’t interested in stubbing out the class we are testing):

Delete Stubs

Finally we want to generate the stubs for the Interfaces, so we need to create a stub of the BaseData assembly:

Generate Data Stubs

Now that the stubs are generated, we can go about changing the parameterized test methods to fit our own purposes. Taking the first method Delete, we make the changes to the method in such a way that the method itself creates the class instance we want to test and the parameter provides us with the boundary conditions. This means that our code changes from this:

  1. /// <summary>Test stub for Delete(Customer)</summary>
  2. [PexMethod]
  3. public void Delete([PexAssumeUnderTest]CustomerManager target, Customer i_customer)
  4. {
  5.     // TODO: add assertions to method
  6.     // CustomerManagerTest.Delete(CustomerManager, Customer)
  7.     target.Delete(i_customer);
  8. }

to this:

  1. /// <summary>Test stub for Delete(Customer)</summary>
  2. [PexMethod]
  3. public void Delete(Customer i_customer)
  4. {
  5.     // Create a stub instance of the ICustomerRepository
  6.     var stub = new SICustomerRepository();
  7.     stub.Delete = (a) => { };
  8.     ICustomerRepository repository = stub;
  9.     // Create a real instance of the CustomerManager that we want
  10.     // to put under test
  11.     CustomerManager manager = new CustomerManager(repository);
  12.     manager.Delete(i_customer);
  13. }

Once all of the parameterized unit tests are prepared we can run the Pex Explorations again:

Run Pex Explorations

The result this time is quite different to what we had previously with the outcome being more conclusive than the first. Upon inspection we actually find some test scenarios that we didn’t cover in our code:

Second Results Pex Explorations

The general idea is to continue to adjust the code of your class to make sure that all of the test cases are handled correctly until you can achieve, as far as possible, test case completeness:

Completed Test Scenario

The next stage is to run the tests to see what the code coverage is:

Completed Code Coverage

You will see that we didn’t get 100% code coverage because of the method “<LoadAllCustomers>b__0(class DiDemo.BaseData.Entities.Customer,class DiDemo.BaseData.Entities.Customer)”;which is the following area of code:

Last five percent

Conclusion

What is on offer by using Pex is quite astounding, but by no means bullet proof. There are still cases that you will want to write your own test method to try to reach the nirvana of 100% code coverage; however what Pex does do is to take care of the majority of cases that you would grudgingly have to knock out by hand.

The other niggling issue in my mind is the fact that the stubs have to be re-generated anytime there is a change made to a dependant class, which might seem like nit-picking, it is still something that could be overlooked during development – thereby rendering the unit test as unreliable.

On the whole I am quite impressed by what is on offer and I could foresee it’s use when the wider .NET development community starts to walk around and kick the tires a bit more, maybe even take it out for a few laps.

  • Share/Bookmark

In continuation with the Dependency Injection entry of my previous blog, now we look at how we can leverage the work we did to use it with a mocking framework to perform proper unit testing.

There are numerous blogs and articles about different Mocking frameworks and after taking a couple of them around the block and kicking the tires I settled on RhinoMocks as the best fit for my needs. I put together a little table to try and capture my thought process on choosing the appropriate framework and although this is not an extensive list it met my needs at the time:

  Ease of use Cost Likes Dislikes Conclusion
RhinoMocks Relatively easy because it’s strongly-typed, which makes the syntax great and"safe". Free Strongly typed instancing – no need to use string references. None so far "Allows for generating a mock object of a class without the interface a requirement. What is powerful about this technique, is that you can request Rhino.Mock to create a mock object of a class (even something like System.Net.WebClient) and then pass that mock object into the implementation of your class.

Now any calls to System.Net.WebClient will be to your mock object even though the target library was compiled to use System.Net.WebClient.

Furthermore, the setting up of method call expectations is compiled, rather than just string names for the methods."
NMock Use of strings to call property methods / normal methods. Free Pales to insignificance in contrast to RhinoMocks or TypeMock Need to use string declarations for instancing. "Requires the object to be mocked so support an interface. Mock objects are generated at runtime to implement the interface. Programmatically you inject method implementations on the mock object so that it expects certain calls and returns values for those calls. This works great until the class you want to mock does not implement an interface."
TypeMock Extremely easy to implement mocking because it uses the .NET framework profiler API to monitor an application’s execution. When a method is called, the CLR notifies Isolator. The framework can then return mocked values and override the original code completely. Expensive The fact that it "plugs-in" to the CLR and captures type references, means that no code changes need to be done on legacy components. Prohibitively Expensive. "Allows for the runtime generation of type objects on the fly within the target project, not just within the test. As a result, you can identify the types you wish TypeMock engine to intercept and then, whenever the target library instantiates those objects, a mock object will be created instead."


If it were not for the pricing this would be the choice (10 out of 10):

Complete Bundle License Per User (12 months): $749.00

Complete Bundle Build Server License (5 VM): $1,999.00

ASP.NET Bundle License Per User (12 months): $599.00

ASP .NET Bundle Build Server License (5 VM): $1,449.00

So what is mocking?

Well, for those readers that are not familiar with mocking it can be summed up with a reference to Martin Fowler in his Mock’s aren’t Stubs post

Mocks are objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

Whilst doing my research into the use of Mocking Frameworks and what I wanted to get out of them I came across a few references to “Isolation Frameworks” that seemed to be used on top of the reference to Mocking Frameworks and the idea is effectively that you use the mocking framework of your choice to isolate the unit under test by specifying the expected behavior of the mocked object. We are still mocking, but with in the context of testing an acute code path of a particular class.

How does it fit in with the example?

In my previous blog, I introduced the very basic example of dependency injection to illustrate the idea behind isolating the dependencies in order to easily “injection” an alternative. Now that we have the ability to inject an alternative, we’ll create mock instances of those dependencies and use those instead of the real ones.

As a reminder, here is the definition of the IEmployeeConnector interface that we created when setting up the Dependency Injection in the EmployeeConnector class:

  1. public interface IEmployeeRepository
  2. {
  3.     /// <summary>
  4.     /// A string containing the connection details for the underlying data repository resource.
  5.     /// </summary>
  6.     string ConnectionString { get; set; }
  7.  
  8.     /// <summary>
  9.     /// Inserts a new Employee instance in to the data repository.
  10.     /// </summary>
  11.     /// <param name="i_employee">The Employee instance to insert.</param>
  12.     void Insert(Employee i_employee);
  13.  
  14.     /// <summary>
  15.     /// Loads (retrieves) an Employee instance from the data repository.
  16.     /// </summary>
  17.     /// <param name="i_employeeId">The unique identifier fro the Employee instance.</param>
  18.     /// <param name="o_employee">The Employee instance retrieved.</param>
  19.     void Load(int i_employeeId, out Employee o_employee);
  20.  
  21.     /// <summary>
  22.     /// Updates an existing Employee instance in the data repository.
  23.     /// </summary>
  24.     /// <param name="i_employee">The Employee instance to update.</param>
  25.     void Update(Employee i_employee);
  26.  
  27.     /// <summary>
  28.     /// Updates an existing Employee instance from the data repository.
  29.     /// </summary>
  30.     /// <param name="i_employee">The Employee instance to delete.</param>
  31.     void Delete(Employee i_employee);
  32.  
  33.     /// <summary>
  34.     /// Retrieves all employee instances  from the data repository.
  35.     /// </summary>
  36.     /// <returns>List of EMployee instances retrieved from the data repository.</returns>
  37.     List<Employee> LoadAllEmployees();
  38. }

With this in mind, we can then test the functionality of the LoadAllEmployees function:

  1. /// <summary>
  2. /// Retruns a sorted list of all employees. Sorted by Last Name.
  3. /// </summary>
  4. /// <returns>List of sorted employees.</returns>
  5. public List<Employee> LoadAllEmployees()
  6. {
  7.     List<Employee> retVal = m_employeeRepository.LoadAllEmployees();
  8.     if (retVal.Count > 0)
  9.     {
  10.         retVal.Sort(delegate(Employee e1, Employee e2)
  11.         {
  12.             return e1.LastName.CompareTo(e2.LastName);
  13.         });
  14.     }
  15.     return retVal;
  16. }

This then implies that we need to create a mock instance of the IEmployeeConnector that will be passed into the constructor of the EmployeeConnector class. The following MSTest method provides an example of how we can do this:

  1. [TestMethod]
  2. public void TestGetSortedEmployeeList()
  3. {
  4.     // Create the mock instance
  5.     IEmployeeRepository eConnect = m_mockRepository.DynamicMock<IEmployeeRepository>();
  6.  
  7.     // Here we create an actual instance
  8.     List<Employee> filledCollection;
  9.     FillCollection(ObjectInstances.FilledInstance, out filledCollection);
  10.  
  11.     // Now we set our expectations
  12.     Expect.Call(eConnect.LoadAllEmployees()).Return(filledCollection);
  13.  
  14.     // Replay our expectations
  15.     m_mockRepository.ReplayAll();
  16.  
  17.     // Create a real instance of the EmloyeeConnector that we want to put under test
  18.     EmployeeManager manager = new EmployeeManager(eConnect);
  19.  
  20.     List<Employee> employees = manager.LoadAllEmployees();
  21.  
  22.     Assert.IsNotNull(employees);
  23.  
  24.     Assert.IsTrue(employees.Count > 0);
  25.  
  26.     Assert.IsTrue(employees[0].LastName == "Anderson");
  27. }

This test method will test the Sort functionality of the LoadAllEmployees function on the EmployeeConnector instance in isolation, because we have told the mocking framework exactly how we want it to behave with the Expect.Call() function.

When the method does get called within the LoadAllEmployees of the EmployerConnector method the RhinoMocks framework will pass back the filledCollection instance that we had constructed:

  1. Expect.Call(eConnect.LoadAllEmployees()).Return(filledCollection);

As a brief note, the Test utility method FillCollection() will fill the passed in List collection with dummy data that the Test method can then check against in the Assert statements:

  1. private static void FillCollection(ObjectInstances instanceType, out List<Employee> filledCollection)
  2. {
  3.     filledCollection = new List<Employee>();
  4.     if (instanceType == ObjectInstances.FilledInstance)
  5.     {
  6.         Employee instance = new Employee();
  7.         instance.Address = "123 Road";
  8.         instance.BirthDate = new DateTime(1975, 3, 15);
  9.         instance.City = "Acity";
  10.         instance.Country = "Acountry";
  11.         instance.EmployeeID = 1234;
  12.         instance.Extension = "5678";
  13.         instance.FirstName = "John";
  14.         instance.HireDate = new DateTime(2005, 5, 21);
  15.         instance.HomePhone = string.Empty;
  16.         instance.LastName = "Doe";
  17.         instance.Notes = string.Empty;
  18.         instance.Photo = string.Empty;
  19.         instance.PostalCode = "01234";
  20.         instance.Region = "Aregion";
  21.         instance.ReportsTo = 1;
  22.         instance.Title = "General Whatsit";
  23.         instance.TitleOfCourtesy = "Mr.";
  24.         filledCollection.Add(instance);
  25.  
  26.         instance = new Employee();
  27.         instance.Address = "123 Street";
  28.         instance.BirthDate = new DateTime(1965, 6, 24);
  29.         instance.City = "Acity";
  30.         instance.Country = "Acountry";
  31.         instance.EmployeeID = 5678;
  32.         instance.Extension = "2003";
  33.         instance.FirstName = "Samantha";
  34.         instance.HireDate = new DateTime(2007, 6, 24);
  35.         instance.HomePhone = string.Empty;
  36.         instance.LastName = "Anderson";
  37.         instance.Notes = string.Empty;
  38.         instance.Photo = string.Empty;
  39.         instance.PostalCode = "01234";
  40.         instance.Region = "Aregion";
  41.         instance.ReportsTo = 1;
  42.         instance.Title = "General Whatchamacallit";
  43.         instance.TitleOfCourtesy = "Ms.";
  44.         filledCollection.Add(instance);
  45.  
  46.         instance = new Employee();
  47.         instance.Address = "405 Avenue";
  48.         instance.BirthDate = new DateTime(1980, 2, 10);
  49.         instance.City = "Acity";
  50.         instance.Country = "Acountry";
  51.         instance.EmployeeID = 9876;
  52.         instance.Extension = "0987";
  53.         instance.FirstName = "Donna";
  54.         instance.HireDate = new DateTime(2000, 4, 18);
  55.         instance.HomePhone = string.Empty;
  56.         instance.LastName = "Kebab";
  57.         instance.Notes = string.Empty;
  58.         instance.Photo = string.Empty;
  59.         instance.PostalCode = "01234";
  60.         instance.Region = "Aregion";
  61.         instance.ReportsTo = 1;
  62.         instance.Title = "Manager";
  63.         instance.TitleOfCourtesy = "Mrs.";
  64.         filledCollection.Add(instance);
  65.     }
  66. }

Visual Studio Test Edition

One of the wonders of the Visual Studio Test Edition is the fact that you can run the MSTests and also specify the Assembly that you want to perform code-coverage:

Visual Studio Code Coverage Dialog

Once this is done, then when you run the MSTest you can view the code paths that were covered by the test (with highlighting):

Code Coverage Results - Covered

And also those code paths that were not covered (with highlighting):

Code Coverage Results - Not covered

  • Share/Bookmark