Channels ▼
RSS

Testing Python and C# Code


Testing C#/.NET Code

I have followed the evolution of the .NET framework and the C# language since the original beta 2. I wrote many programs at home and published several articles on the framework and language, but until recently, I never had the the chance to use them in a professional capacity. These days, I work for Roblox, where I write highly scalable and highly distributed services using C# and the .NET framework, and I enjoy every minute. C# is a wonderful programming language. It started out as a better Java, and in each revision, it has added more and more good stuff (generics, anonymous functions, attributes, LINQ, async support) with great balance. For some reason (great language designers?), the power and richness of C# never gave me a feeling of bloat like other languages do. When you throw in the .NET framework and Visual Studio, you get a superb development and runtime environment.

Visual Studio provides a good unit test framework (Microsoft.VisualStudio.TestTools.UnitTesting), which you can configure with attributes. It allows you to run any subset of tests in your solution. My go-to technique is to place the mouse cursor on a test I want to run and then click CTRL+R+T to launch just that test. In the past, I used to write little console programs to exercise different parts of the code. Not anymore.

While there are also many other great test frameworks (both commercial and free), I will use the built-in Visual Studio support in the following examples, but the concepts are not related to any particular test framework.

Reflection

The .NET framework keeps excellent metadata for every object and allows reflection. You can take any object even without knowing its type (all objects are derived from the object class) and, at runtime, discoverits fields, properties, methods, etc. You can also access fields and properties, and invoke methods. The most important thing about reflection is that it allows you to ignore access modifiers. You can access private state and invoke private methods, access internal types, as well as instantiate private classes and nested classes. The following example shows a class A with a private data member _x and a private method DoubleIt():

class A
	{
		private int _x;

		public A(int x = 5)
		{
			_x = x;
		}

		private void DoubleIt()
		{
			_x *= 2;
		}
	}

Class A happens to be a nested class. This is important because, when accessing nested classes, you qualify their name with +. If you are not sure what is the exact type name of the class you want to test, you can use the GetTypes() of the assembly to list all the types it contains. Here is a unit test for class A using raw reflection:

[TestMethod]
	public void RawReflectionTest()
	{
		var assembly = GetType().Assembly;

		// Get all the types in the assembly
		var types = assembly.GetTypes();
		
		var typeName = "ReflectionDemo.ReflectionTest+A";
		var t = assembly.GetType(typeName);
		var instance = Activator.CreateInstance(t, 4);

		// Get the value of X
		var filed = t.GetField("_x", BindingFlags.NonPublic | BindingFlags.Instance);
		var value = filed.GetValue(instance);
		Assert.AreEqual(4, value);

		// Invoke DoubleIt
		var method = t.GetMethod("DoubleIt", BindingFlags.NonPublic | BindingFlags.Instance);
		method.Invoke(instance, new object[] {});

		// Verify result
		value = filed.GetValue(instance);
		Assert.AreEqual(8, value);
	}

Obviously, it is pretty cumbersome, so I created a little utility class called ReflectedObject that makes it a little more straightforward. Here is the same test using ReflectedObject:

[TestMethod]
	public void ReflectedObjectTest()
	{
		var assembly = GetType().Assembly;
		var typeName = "ReflectionDemo.ReflectionTest+A";
		var instance = new ReflectedObject(assembly, typeName, 4);

		var value = instance.GetField("_x");
		Assert.AreEqual(4, value);

		instance.InvokeMethod("DoubleIt");

		value = instance.GetField("_x");
		Assert.AreEqual(8, value);
    }

If you're using .NET 4.x, it is possible to go even further using the DynamicObject class, then writing a subclass that allows you to directly access private fields, properties, and methods.

I recently worked on a effort to integrate RabbitMQ into Roblox's technology stack. RabbitMQ is an awesome message queue with built-in clustering support. The RabbitMQ C# Client provides a full-featured API to talk to the RabbitMQ server. Unfortunately, it is not cluster-aware. If you want to talk to a cluster and handle situations like nodes going up and down, temporary disconnects, and recovery, you need to write a lot of extra code. I did just that and I wanted to test it.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video