Fairway Technologies Blog

blog

Avoid Hard-Coding in Unit Tests

In Development No Comments

In my previous blog post entitled Introduction to Unit Testing with Visual Studio, I introduced you to creating unit tests with Visual Studio. A method named FileExists was created to which you pass a file name to see if it exists. In the tests you created, you use hard-coded file names to test. Just as you wouldn’t hard-code values in a normal application, you should not do this with unit tests either. In this blog post you will learn to use constants, a configuration file, and how to create and delete test files.

Please go read the previous blog post and create the project, or download the project at http://www.pdsa.com/downloads and select “Introduction to Unit Testing” from the list.

Use a Constant

Constants are a great way to centralize hard-coded data that would otherwise be repeated throughout an application. In this case, you are going to replace the hard-coded file name used in the FileNameDoesNotExist method with a constant. At the top of the FileProcessTest class, add the following constant.

<pre>private const string BAD_FILE_NAME = @"C:\NotExists.bad";</pre>

Modify the FileNameDoesNotExist to use this new constant as shown in the code snippet below.

<pre>public void FileNameDoesNotExist() {<br>&nbsp; FileProcess fp = new FileProcess();<br>&nbsp; bool fromCall;<br>&nbsp; fromCall = <span style="font-weight: bold;">fp.FileExists(BAD_FILE_NAME);</span><br>&nbsp; Assert.IsFalse(fromCall);<br>}</pre>

Use a Configuration File

A constant is a good option for the “bad” file name. For the “good” file name you wish to test to see exists, let’s add that to a configuration file so it can be modified easily. In fact, let’s add a replaceable token called [AppPath] that will figure out the appropriate path to use based on the machine the test is running upon.

Right mouse click on the FileProcessTest project and select Add | New Item… from the menu. From the template dialog select General | Application Configuration File. The name should already be set to App.config, so click on the Add button.

Within the <configuration> element add an <appSettings> section. Within the <appSettings> section add a key called GoodFileName with a value of [AppPath]\TestFile.text as shown below.

<pre>&lt;appSettings&gt;<br>&nbsp; &lt;add key="GoodFileName" value="[AppPath]\TestFile.txt"/&gt;<br>&lt;/appSettings&gt;</pre>

In order to retrieve this value from the configuration file, you need to use the ConfigurationManager class from the System.Configuration namespace. By default, the System.Configuration DLL is not added to a test project. Right mouse click on References folder in your FileProcessTest project and select Add Reference from the menu. From the dialog select Assemblies | Framework. Locate the System.Configuration dll and select the check box. Click the OK button to add this DLL to your test project.

At the top of the FileProcessTest class, add a using statement for the System.Configuration namespace.

<pre>using System.Configuration;</pre>

Add private field to FileProcessTest class to hold the value you are going to retrieve from the configuration file. Set the name of this field to _GoodFileName as shown below.

<pre>private string _GoodFileName;</pre>

Add a constructor to the FileProcessTest class in which you will read the GoodFileName value from the configuration file. Within this constructor you will replace the token [AppPath] with the value from the Environment.SpecialFolder.ApplicationData. This enumeration, supplied by .NET, when passed to the GetFolderPath, returns the pre-defined path for any data you need to store for this application. The location may vary from OS to OS, but on Windows 10 it is C:\\Users\\YOUR_USERNAME\\AppData\\Roaming. Write the constructor as shown below.

<pre>public FileProcessTest() {<br>&nbsp; _GoodFileName =<br>&nbsp; &nbsp; &nbsp;ConfigurationManager.AppSettings["GoodFileName"];<br>&nbsp; if (_GoodFileName.Contains("[AppPath]")) {<br>&nbsp; &nbsp; &nbsp;_GoodFileName = _GoodFileName.Replace("[AppPath]",&nbsp;<br>&nbsp; &nbsp; &nbsp; &nbsp; Environment.GetFolderPath(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Environment.SpecialFolder.ApplicationData));<br>&nbsp; }<br>}</pre>

Locate and modify the FileNameDoesExist method to use this new property name.

<pre>[TestMethod]<br>public void FileNameDoesExist() {<br>&nbsp; FileProcess fp = new FileProcess();<br>&nbsp; bool fromCall;<br>&nbsp; fromCall = fp.FileExists(<span style="font-weight: bold;">_GoodFileName</span>);<br>&nbsp; Assert.AreEqual(true, fromCall);<br>}</pre>

Create / Delete File

Instead of you having to create the file name in the location specified, prior to running the FileNameDoesExist test, you should create the file name within the method itself. You should delete the file after you have performed the FileExists call so you don’t keep files around you don’t need. Modify the FileNameDoesExist method to look like the following.

<pre>[TestMethod]<br>public void FileNameDoesExist() {<br>&nbsp; FileProcess fp = new FileProcess();<br>&nbsp; bool fromCall;<br>&nbsp;<span style="font-weight: bold;"> if (!string.IsNullOrEmpty(_GoodFileName)) {<br>&nbsp; &nbsp; // Create the 'Good' file.<br>&nbsp; &nbsp; File.AppendAllText(_GoodFileName, "Some Text");<br>&nbsp; }</span><br>&nbsp; fromCall = fp.FileExists(_GoodFileName);<br>&nbsp;<span style="font-weight: bold;"> // Delete file<br>&nbsp; if (File.Exists(_GoodFileName)) {<br>&nbsp; &nbsp; File.Delete(_GoodFileName);<br>&nbsp; }</span><br>&nbsp; Assert.IsTrue(fromCall);<br>}</pre>

TestContext

When the unit test framework creates an instance of a test class (a class marked with the [TestClass] attribute), the test framework creates a TestContext object. This object contains properties and methods related to testing. To access this TestContext object, you must create a public property named TestContext in each of your test classes. The test framework checks for this property and inserts the instance of this TestContext into your property.

<pre>private TestContext _TestInstance; &nbsp;<br>public TestContext TestContext &nbsp;<br>{ &nbsp;<br>&nbsp; &nbsp; get { return _TestInstance; } &nbsp;<br>&nbsp; &nbsp; set { _TestInstance = value; } &nbsp;<br>}</pre>

One of the things you can do with this TestContext is to use the WriteLine method to add some output into the test results. Add the lines shown in bold below to write some messages about what file you are creating into the output area of the test results.

<pre>[TestMethod]<br>public void FileNameDoesExist() {<br>&nbsp; FileProcess fp = new FileProcess();<br>&nbsp; bool fromCall;<br>&nbsp; if (!string.IsNullOrEmpty(_GoodFileName)) {<br>&nbsp; &nbsp; <span style="font-weight: bold;">TestContext.WriteLine("Creating file: " + _GoodFileName);</span><br>&nbsp; &nbsp; // Create the 'Good' file.<br>&nbsp; &nbsp; File.AppendAllText(_GoodFileName, "Some Text");<br>&nbsp; }<br>&nbsp; <span style="font-weight: bold;">TestContext.WriteLine("Checking file: " + _GoodFileName);</span><br>&nbsp; fromCall = fp.FileExists(_GoodFileName);<br>&nbsp; // Delete file<br>&nbsp; if (File.Exists(_GoodFileName)) {<br>&nbsp; &nbsp; <span style="font-weight: bold;">TestContext.WriteLine("Deleting file: " + _GoodFileName);</span><br>&nbsp; &nbsp; File.Delete(_GoodFileName);<br>&nbsp; }<br>&nbsp; Assert.IsTrue(fromCall);<br>}</pre>

Run this test, once it is complete, click on the FileNameDoesExist test in the Test Explorer window, locate the Output link at the bottom of the window and click on it. You should then see your messages appear in an output window as shown in Figure 1.

UnitTest02-Fig1.png

Figure 1: Write output messages using the TestContext property

Summary

In this blog post a constant was used in place of a hard-coded file name. You placed another hard-coded file name into the App.config file in the test project. Within a test method you created a file, tested that file’s existence, then deleted the file. This helps keep that method self-contained and avoids manual setup of files prior to running these tests. Finally, you added a TestContext property to access the WriteLine method of the TestContext property. This allows you to add additional messages into the output of the test results.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Avoid Hard-Coding in Unit Tests.

Plan Your Technology Team with This Online Assessment Tool

Subscribe to Email Updates

Build a Technology Dream Team. Start with This Online Assessment. →