Jun 06 2008

VSTS Testing Private Members and Methods using Accessors

Published by Raja Nadar under unit testing

as explained in my previous post, we can easily test the public methods of a class. At the end of that blog, I mentioned about private accessors. this post explains that cool feature.

assume we have a class as follows:

 

public class BusinessObject
{
    private const int MagicFactor = 18;
 
    private int CalculateConfidentialValue(int baseAmount)
    {
        return baseAmount * BusinessObject.MagicFactor;
    }
}

obviously, the method and magic factor are not super-confidential or anything. but let us pretend, they are.

here both the constant and the method are private. needless to say, the following Test Method won’t compile.

 

[TestMethod()]
public void WontCompileCalculateConfidentialValueTestMethod()
{
    BusinessObject myObject = new BusinessObject();
 
    int magicFactor = BusinessObject.MagicFactor;
 
    Assert.AreEqual(18, magicFactor);
    Assert.AreEqual(magicFactor * 2, myObject.CalculateConfidentialValue(2));
}

this is where private accessors help us. Private Accessors help us to access the Private members and Methods of an object, so that we can unit test them.

  1. go to the BusinessObjects class file
  2. Right Click >> Create Private Accessor >> Test project..

something similar is created behind the scenes:

 

[Shadowing("PrivateAccessorDemo.BusinessObject")]
public class BusinessObject_Accessor : BaseShadow
{
    protected static PrivateType m_privateType;
 
    [Shadowing(".ctor@0")]
    public BusinessObject_Accessor();
    public BusinessObject_Accessor(PrivateObject __p1);
 
    [Shadowing("MagicFactor")]
    public static int MagicFactor { get; }
    public static PrivateType ShadowedType { get; }
 
    public static BusinessObject_Accessor AttachShadow(object __p1);
    [Shadowing("CalculateConfidentialValue@1")]
    public int CalculateConfidentialValue(int baseAmount);
}

you don’t need to try and understand that. all you need is how to use the created code, and here it is:

 

[TestMethod()]
public void CalculateConfidentialValueTestMethod()
{
    BusinessObject_Accessor target = new BusinessObject_Accessor();
 
    int magicFactor = BusinessObject_Accessor.MagicFactor;
 
    Assert.AreEqual(18, magicFactor);
    Assert.AreEqual(magicFactor * 2, target.CalculateConfidentialValue(2));
}

As we can see, we could now test the Private Member and the Private Method using the accessor.

Another good thing about the private accessors now, is the way we can test for exceptions thrown by the method.

Assume we modify our source method to:

 

public class BusinessObject
{
    private const int MagicFactor = 18;
 
    private int CalculateConfidentialValue(int baseAmount)
    {
        if (baseAmount == 0)
        {
            throw new ArgumentException("argument cannot be zero.", "baseAmount");
        }
 
        return baseAmount * BusinessObject.MagicFactor;
    }
}

To test, if an argument exception is indeed thrown, we could write an unit test case as follows:

 

[TestMethod()]
[ExpectedException(typeof(System.ArgumentException))]
public void CalculateConfidentialValueWithZeroParameterTestMethod()
{
    BusinessObject_Accessor target = new BusinessObject_Accessor();
    target.CalculateConfidentialValue(0);
}
  1. I believe this is possible, because of the Shadowing attribute which is now used for private accessors changing the way exceptions are returned by the target object.
  2. Before the Shadow based reflection, the accessor always threw a System.Reflection.TargetInvocationException and the inner exception needed to be evaluated.
  3. The concise attribute based exception check for ArgumentException could not be checked.

 

Private Accessors are very useful, when you are testing the Business Layer or OM.

there’s a solution to every problem; given enough time and money..

5 responses so far

May 24 2008

VS unit testing suite

Published by Raja Nadar under unit testing

as you know, VS introduced its own unit testing framework, inbuilt for TDD. there are some very good things I like about it.

  1. it removes the dependency from any external unit testing libraries like NUnit. (though NUnit was worth all its bits)
  2. it is part of the build process and we can use the VSTS test runner (MSTest.exe), if the test cases need to be run separately, rather than from the IDE.
  3. Code coverage can be done using the test config file.
  4. it also provides support for deploying the required support files for the test cases to run successfully. (xml files, config files etc) explained later.
  5. the code can be debugged using the test cases.
  6. regression testing is very reliable, when features change in your application.
  7. The test cases can be organized using the ‘Test Manager’

 Let us take a small and simple example. I have a sample ‘Add’ method which I want to test:

 

public int Add(int a, int b)
{
    return a + b;
}

To create a Unit test for this method/project, you could just right click inside the file containing this method >> Create Unit Tests and you can have the whole test project template within seconds.

The Unit Test is as follows:

 

[TestMethod()]
public void AddTest()
{
    Calculator target = new Calculator();
 
    int a = 8;
 
    int b = 16;
 
    int expected = 24;
    int actual;
 
    actual = target.Add(a, b);
 
    Assert.AreEqual(expected, actual);
}

 

  • The Test class is attributed with the [TestClass()]attribute.
  • The Test method is attributed with the [TestMethod()]attribute.
  • There are also some other interesting attributes at the Test Method level:
  • ExpectedException(typeof(Exception))] >> This attribute makes the unit case, expect the type of exception provided, and does not fail the test case when that exception occurs. This is usefule if there are -ve test cases. (E.g FileNotFoundException, Business Exceptions etc)
  • [Description("Test Case Id: 2.34 Add two valid numbers.")] >> This attribute makes it easier to name a test case according to your test case list. Whenever the test case fails, you can see the Description and identify the exact test case which failed.
  • [WorkItem(9999)] >> This attribute is helpful in associating the test case with a work item in your scrum/tfs/project management tool.
  • [Ignore()] >> If there are some test cases, which need not be executed at this point of time, this attribute can be used.

 TestSolution.vsmdi

  • This file is basically what the test manager controls. It is used to group all the test cases in the solution.
  • As you notice, this file is a solution level item and hence common to all the project unit cases in your application.
  • You can create ‘New Test Lists’ and arrange your test cases as per Projects/Business Objects/Layers etc.
  • Once arranged, the specific test cases can be executed.

localtestrun.testrunconfig

  • This is the common configuration file for all the test cases in your application.
  • It controls the naming scheme for the output folder it creates, where the test run results are stored.
  • Code Coverage can be enabled for the assemblies.
  • The Deployment option provides the support files, which you may want to deploy for the test cases to run successfully.
  • It also provides a ‘Startup’ and ‘Cleanup’ script option to be run, before and after the test case run.

there are other cool features of the Unit Testing framework like, reading test data from data sources, private accessors, web tests, load testing, linking it to external test controllers and agents etc..

 there’s a solution to every problem; given enough time and money..

 

3 responses so far