Running Python within a C# Unit Test


The Requirements

My first programming language was Python. Whenever I get the chance, I like to pull out the old skills and code Python again. A couple weeks ago, we had a customer that has an entire test suite written in Python. They were looking to refactor their test process to be better automated, and have better reporting. My first thought was to migrate toward the MSTest framework, but it was tempered with my principle of re-use code where possible. With this in mind, I was faced with a challenge of getting python to work within the confines of MS Test. Although this challenge proved to be trivial in the end, it allowed me to explore the advanced features of the .NET platform.

With the advent of .NET Framework v4.0 (Visual Studio 2010), Microsoft released the DLR (Dynamic Language Runtime). The DLR is a set of services that sit on top of the CLR (Common Language Runtime)and add the ability to run Dynamic programming languages. For example, JavaScript has been ported to the DLR (IronJS), Ruby has been ported (IronRuby), but the most important language (for the purpose of this blog) is the Python implementation (IronPython). One nice feature about languages written against the DLR is their ability to interact with .NET Libraries, and for CLR languages to interact with DLR components.

The Implementation of Python

So how can you get a Python test to interact with the UnitTestFramework assembly and the testing engine? The .NET framework namespace IronPython.Hosting contains the classes and methods needed to create the IronPython runtime, and execute the code. To create the runtime, you need to write the following code:


var path = @"C:\devel\IronPython_Library.py"
var ipy = Python.CreateRuntime();
var ipyScope = ipy.UseFile(path);

The method Python.CreateRuntime() creates the IronPython Runtime, while ipy.UseFile() loads the IronPython code into the runtime for execution. The object type for ipyScope is dynamic, which comes in handy when we go to execute the IronPython code. The IronPython code contained in the file is the following:


import System
from System.Collections import *

#
# IronPython Example
#

class CollectionWrapper():
""" Wraps an ArrayList """

def __init__(self):
  """Constructor"""
  self._collection = ArrayList()

def AddToCollection(self, item):
  """Adds the item to the collection"""
  self._collection.Add(item)

def IsInCollection(self, item):
  """Validates if the item is in the collection"""
  return self._collection.Contains(item)

The class CollectionWrapper contains an ArrayList, and contains methods to add an item to the ArrayList, and check to make sure the item is contained in the ArrayList. This simple example outlines the ability for IronPython to interact with native .NET Libraries. C# has the reciprocal ability in that we can instantiate IronPython classes, and interact with the code as if it was managed C#. The code below demonstrates this ability:


var item = "p";
var cw = ipyScope.CollectionWrapper();
cw.AddToCollection(item);
var added = cw.IsInCollection(item);

The Payoff

The CollectionWrapper class is instantiated, and assigned to cw. The next step is to add item into the collection, then validate that item has been added to the collection. Since we can interact with the Python code as if it was a C# library, we have a way to wrap Python code into a C# Unit Test. This provides a structured approach to testing, as we can leverage the MSTest framework for execution and reporting to streamline the functional testing the customer runs in Python. Here is a full example of the C# Unit Test created to run IronPython.


using IronPython.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IronPython_UnitTest
{
[TestClass]
public class UnitTest1
{
  dynamic ipyScope;

  [TestInitialize]
  public void InitializationMethod()
  {
    var path = @"C:\devel\IronPython_Library.py";
    var ipy = Python.CreateRuntime();
    ipyScope = ipy.UseFile(path);
  }

  [TestMethod]
  public void TestCollectionWrapperAdd()
  {
    var item = "p";
    var cw = ipyScope.CollectionWrapper();
    cw.AddToCollection(item);
    var added = cw.IsInCollection(item);
    Assert.IsTrue(added, "Item was not successfully Added to Collection");
  }
 }
}

Comments (0)

Skip to main content