Thursday, July 22, 2010

Data Driven testing using MbUnit v3

I was reading about data driven testing using mbunit from this article. I am using v3 of mbunit now and the attributes used for DataDriven testing are not applicable in v3 as per the release notes for v3.

Using this article, I was able to do data driven testing using xml data source. But I had to use [Bind("price")] with each parameter.  What I wanted was a way to deserialize the Xml test data and run the test cases as many number of times as my test data. If you read the article by Ben Hall, it was posiible in v2 using ForEachTest attribute which is not supported in v3.

So I tried to do the following as a workaround.
First of all create an Xml data source. Change the settings to make it an embedded resource.
<ArrayOfTestData>
  <TestData TestCaseNumber="1">
    <Name>Abc</Name>
  </TestData>
</ArrayOfTestData >

Create the class to deserialize the test data
[Serializable]
    public class TestData
    {
        [XmlAttribute("TestCaseNumber")]
        public int TestCaseNumber { get; set; }
        public string Name { get; set; }
    }

Now we need to deserialize the xml file. I have done it in the SetUp method so that this operation is done once for all the test cases.

I have used the feature of MbUnit to create test cases dynamically.  So for doing that you need to apply the attribute [DynamicTestFactory]  to a method which will create test cases dynamically based on the test data in your xml file.
[TestFixture]
    public class SampleTestFixture
    {
        private List<TestData> testDataList;

        [SetUp]
        public void SetUp()
        {
            XmlDocument document = new XmlDocument();
            Assembly currentAssembly = Assembly.GetExecutingAssembly();
            document.Load(currentAssembly.GetManifestResourceStream("SampleProject.TestData.xml"));

            //TODO: Implement your deserialization code
            testDataList = Deserialize<List<TestData>>(document.InnerXml);
        }

        [TearDown]
        public void TearDown()
        {
            testDataList = null;
        }

        [DynamicTestFactory]
        public IEnumerable<Test> CreateTests()
        {
            foreach (TestData testData in testDataList)
            {
                yield return new TestCase("Test case " + testData.TestCaseNumber, () =>
                    {
                        //TODO: Add your test logic and Asserts
                    });
            }
        }       
    }  

Please feel free to suggest any better solution to handle this case.

Sunday, March 21, 2010

Refresh Functionality in Caching Application Block of Enterprise Library

In Enterprise Library Caching Application Block, a Cache can be created by using the following code-

CacheFactory.GetCacheManager().Add("TestKey", "TestValue",
CacheItemPriority.Normal, null,
new AbsoluteTime(TimeSpan.FromSeconds(5)));

The fourth parameter has been passed as null in the above example so that the Cache will not be refreshed once it is expired.


The expiration policy can be of Sliding Time, Absolute Time, Extended Time Format, File Dependency or Never Expired. In the above example Absolute Time expiration policy has been used.


The Cache value can be retrieved by using the following code-

CacheFactory.GetCacheManager().GetData("TestKey")


If the Cache needs to be refreshed automatically, the Refresh method of ICacheItemRefreshAction interface should be implemented.

class TestCacheItemRefreshAction : ICacheItemRefreshAction
{
   #region ICacheItemRefreshAction Members

   void ICacheItemRefreshAction.Refresh(
    string removedKey, object expiredValue,
    CacheItemRemovedReason removalReason)  
   {
       CacheFactory.GetCacheManager().Add(
        "TestKey", "TestValue",   
        CacheItemPriority.Normal,
        new TestCacheItemRefreshAction(),
        new AbsoluteTime(TimeSpan.FromSeconds(5)));
   }

   #endregion

}

When ICacheItemRefreshAction interface is implemented, then an object of the class implementing this interface should be passed instead of null as the refresh instruction while creating the Cache.

CacheFactory.GetCacheManager().Add("TestKey", "TestValue",
CacheItemPriority.Normal, new TestCacheItemRefreshAction(),
new AbsoluteTime(TimeSpan.FromSeconds(5)));

When the above code is executed, there are chances that at few times, instead of the Cache value, null value is returned. The reason for this is when GetData() method is called, it checks for the expiry of the Cache object. If it has expired, then it calls the Refresh() method of the class which implements ICacheRefreshAction. But during this refresh period it keeps the old copy of the Cache object so that applications using the Cache can still get the old value. Internally it makes a copy of the Cache and updates the same. Once updating is complete it then updates the real Cache with the updated value.


But the Enterprise Library Code shows that the refresh call happens through a delegate (i.e. asynchronously). So, if the calling thread moves ahead and tries to retrieve the Cache value during the time when the Cache object is getting refreshed, null value is returned.


The following code can be used to avoid getting null value and at the same time refresh the Cache automatically.


Add Cache using the following code-

CacheFactory.GetCacheManager().Add("TestKey", "TestValue",
CacheItemPriority.Normal, null,
new AbsoluteTime(TimeSpan.FromSeconds(5)));


In the above code, null is passed in place of the object of class implementing ICacheItemRefreshAction interface because the refresh machanism is implemented without using this interface.


Get data using the following code-

String value = Convert.ToString(CacheFactory.GetCacheManager().GetData("TestKey"));


if (String.IsNullOrEmpty(value))
{
   ReloadCache();

   value = Convert.ToString(CacheFactory.GetCacheManager().GetData("TestKey"));

}

Refresh Cache using the following code-

private void ReloadCache()
{

   CacheFactory.GetCacheManager().Add("TestKey", "TestValue",
   CacheItemPriority.Normal, null,
   new AbsoluteTime(TimeSpan.FromSeconds(5)));
}


In the above machanism if the value retrieved from the Cache is null value, then the Cache is refreshed and the value is retrieved again.


This mechanism helps to avoid getting null value by simulating a custom refresh implementation.