Tag Archives: Unit testing

Test Method Snippet

I created a useful code snippet that I thought I would share. The shortcut is tm and the snippet outputs the following text, giving you the skeleton for a unit test:

[TestMethod]
public void TestMethod()
{
	//  Arrange
	
	//  Act
	
	//  Assert                        
	
}

You can download that snippet by clicking the following button:

Also, I got sick of typing mvcaction4 every time I wanted to scaffold out an Action Method. So I cracked open the snippet file for that snippet using the a text editor and changed the contents of the shortcut element to mac4. Much easier to type and very easy to remember as well!

Unit Testing Private Methods

Some very smart and experienced people say not to test private methods. Fair enough. The reasons are good – read here.

This post is not going to buy into that debate. It’s purpose is to demonstrate how to test private methods. The answer is painfully obvious – use reflection.

In my StringExtensions class, I have a private static helper method called GetIndexOfNthCharHelper. I wanted to unit test it (hey it’s my API and I can control it, so some of the Just Say No reasons go away). This is the test I wrote:

[TestMethod]
public void GetIndexOfNthCharHelperDoes()
{
	//  Arrange
	var type = typeof (StringExtensions);
	var getIndexOfNthCharHelperMethod = type.GetMethod("GetIndexOfNthCharHelper", BindingFlags.NonPublic | BindingFlags.Static);

	//  Act
	int index = (int)getIndexOfNthCharHelperMethod.Invoke(null, new object[] { path, 2, 0, Path.DirectorySeparatorChar });

	//  Assert                        
	Assert.AreEqual(15, index);
}

Not too complex. But I do heed the warnings of others and definitely wouldn’t be too keen to unit test a private method of a 3rd party API over which I have no control.

ExceptionAssert.Throws instead of ExpectedException

Jimmy Bogard has already written about the perils of using the ExpectedException attribute in unit testing (MS Test and NUnit have that attribute). NUnit has dealt with this by including an Assert.Throws<T> method (it also has an Assert.Catch<T> method).

My testing framework of choice (MS Test) does not have an equivalent (disappointing). So, I set about writing one. The implementation looks like this:

public static class ExceptionAssert
{
        public static void Throws<T>(Action code, string exceptionMessage = null, string message = null, params object[] args) where T : Exception
        {
            try
            {
                code.Invoke();

                Assert.Fail("No exception was thrown by the code under test.");
            }
            catch (Exception exception)
            {
                if (string.IsNullOrWhiteSpace(exceptionMessage))
                {
                    Assert.AreEqual(exception.GetType(), typeof(T), message, args);
                }
                else
                {
                    Assert.AreEqual(exception.GetType(), typeof(T), message, args);
                    Assert.IsTrue(exception.Message.StartsWith(exceptionMessage, StringComparison.OrdinalIgnoreCase));
                }
            }
        }
}

And using it in a unit test as so:

[TestMethod]
[TestCategory(TestCategories.MembershipIntegration)]
public void CreateUserAccountWithNoTenantThrowsException()
{
	var membershipService = new MembershipService();

	ExceptionAssert.Throws<ArgumentNullException>(
		() =>
			membershipService.CreateUser(null, 
			TestFirstName, 
			TestLastName, 
			TestFirstName + " " + TestLastName,
			TestPassword, TestEmail
			));
}

Now, we won’t get the false negative in the event that the exception is thrown in the constructor of the MembershipService class, rather than in the method under test, membershipService.CreateUser.

If you want to verify the text of the exception message, you can do that too:

[TestMethod]
[TestCategory(TestCategories.MembershipIntegration)]
public void CreateUserAccountWithNoTenantThrowsException()
{
	var membershipService = new MembershipService();

	ExceptionAssert.Throws<ArgumentNullException>(
		() =>
			membershipService.CreateUser(null, 
			TestFirstName, 
			TestLastName, 
			TestFirstName + " " + TestLastName,
			TestPassword, TestEmail
			),
			"The argument was null"
			);
}

I’ve also included parameters for a failure message and formatting of that message (which is a normal overload of the Assert methods).

Act Arrange Assert Comment Snippet

When I write unit tests, I like to quickly add the following commenting or scaffolding:

[TestMethod]
public void NameOfTestMethod()
{
	//  Arrange

	//  Act

	//  Assert     
}

As I’m always on the lookout to streamline my workflows, I created a Visual Studio snippet for that commenting. The text of the snippet is:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>Act Arrange Assert</Title>
   <Shortcut>aaa</Shortcut>
   <Description>Code snippet for Act, Arrange Assert text.</Description>
   <Author>Dave</Author>
  </Header>
  <Snippet>
   <Code Language="csharp"><![CDATA[//  Arrange

            //  Act

            //  Assert                        
		]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>

There’s a couple of ways of installing that snippet:

  1. Copy that text into a text editor and save it as a file with the snippet file extension into the relevant directory for snippets in your environment (I called mine aaa.snippet) . For me, that was
    E:\Documents\Dave\Documents\Visual Studio 2013\Code Snippets\Visual C#\My Code Snippets
  2. Open the Code Snippets Manager (Tools > Code Snippets Manager) and import it via that GUI:

    CodeSnippetsManager

To use the snippet, you just type the letters aaa then immediately hit the tab key twice. Also, you will need to restart Visual Studio (if it was already open when you saved the snippet).

DeploymentItem Attribute and UnitTesting With Visual Studio

I set out to do some unit testing today and forgot an important step which is required to use the DeploymentItem attribute. To set the scene, the method I was testing retrieved a FileStream in its internals. That is, the FileStream was not the returned object; it was something which the method required to do its thing. And I wasn’ going to unit test the .NET Framework method that gets the FileStream as that would be quite pointless.
The reason the Deployment Item attribute here was a great option was because it auto-magically took care of the the File IO and let me focus on the method that a I was unit testing. That method was simply:

public Stream GetFileStream(string path)
{
	if (ReferenceEquals(path, null))
		throw new ArgumentNullException("path");

	if (File.Exists(path))
		return File.OpenRead(path);

	throw new FileNotFoundException(string.Format("There is no file that corresponds with the following path: {0}", path));
}

So, I went ahead and wrote the test:

[TestMethod]
[DeploymentItem(@"KesselRun.IoTests\Templates\aTemplate.txt", "Templates")]
public void GetFileStream_Returns_FileStream()
{
	var stream = _textFileIo.GetFileStream(@"Templates\aTemplate.txt");
	
	Assert.IsNotNull(stream);
	Assert.IsInstanceOfType(stream, typeof(System.IO.FileStream));
}

The only problem was, the text file aTemplate.txt was not being deployed and I was not getting an object for my FileStream. There was one thing I had forgotten to do. I had to edit the settings for the test to enable deployments. That is done by selecting the Edit Test Settings item from the Test menu (make sure you get the correct one for your test project):TestSettingsEdit
Then:

  1. click the Deployment item in the ListBox on the left;
  2. check the Enable deployment checkbox; and
  3. add the file that you want deployed (that file was actually part of my project, as you can see in the Solution Explorer part of the screen-capture below).

EnableDeployments

There are a lot of posts around the Internet where developers have written about using embedded files as deployment items for unit tests, rather than the auto-magic mechanism described in this post. For the most part, I agree with that. But in this particular scenario, where a FileStream has to be part of the method under test (and which cannot be mocked), the DeploymentItem attribute is the way to go.