Given this code
using System;
using System.Configuration;
using System.IO;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
using (var writer = new StringWriter())
{
new SeasonWriter().WriteSeason(writer);
Console.WriteLine(writer);
Console.ReadLine();
}
}
}
public class SeasonWriter
{
public void WriteSeason(TextWriter textWriter)
{
var month = ConfigurationManager.AppSettings["Month"];
switch (month)
{
case "January":
case "February":
case "December":
textWriter.Write("Winter");
break;
case "March":
case "April":
case "May":
textWriter.Write("Spring");
break;
case "June":
case "July":
case "August":
textWriter.Write("Summer");
break;
case "September":
case "October":
case "November":
textWriter.Write("Autumn");
break;
default:
throw new ConfigurationErrorsException("No setting defined for 'Month'."
+ Environment.NewLine
+ "Please add a setting to the .exe.config file under the section AppSettings."
+ Environment.NewLine
+ "For more info on the .exe.config file, follow this link "
+ "http://msdn.microsoft.com/en-us/library/vstudio/1fk1t1t0(v=vs.110).aspx");
}
}
}
}
In order to get tests around the SeasonWriter class, the AppSettings entry "Month" needs to be faked or abstracted. Some options are:
using System.Configuration;
using System.IO;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
using (var writer = new StringWriter())
{
new SeasonWriter().WriteSeason(writer);
Console.WriteLine(writer);
Console.ReadLine();
}
}
}
public class SeasonWriter
{
public void WriteSeason(TextWriter textWriter)
{
var month = ConfigurationManager.AppSettings["Month"];
switch (month)
{
case "January":
case "February":
case "December":
textWriter.Write("Winter");
break;
case "March":
case "April":
case "May":
textWriter.Write("Spring");
break;
case "June":
case "July":
case "August":
textWriter.Write("Summer");
break;
case "September":
case "October":
case "November":
textWriter.Write("Autumn");
break;
default:
throw new ConfigurationErrorsException("No setting defined for 'Month'."
+ Environment.NewLine
+ "Please add a setting to the .exe.config file under the section AppSettings."
+ Environment.NewLine
+ "For more info on the .exe.config file, follow this link "
+ "http://msdn.microsoft.com/en-us/library/vstudio/1fk1t1t0(v=vs.110).aspx");
}
}
}
}
Create an app.config file in the test project
This is the most obvious starting point. Create a test project to contain the SeasonWriterTests file, with an app.config containing a "Month" entry:
<appSettings>
<add key="Month" value="January"/>
</appSettings>
<add key="Month" value="January"/>
</appSettings>
using NUnit.Framework;
using System.IO;
namespace Demo.Test
{
[TestFixture]
public class SeasonWriterTests
{
[Test]
public void WriteSeasonExpectWinter()
{
var writer = new StringWriter();
var target = new SeasonWriter();
target.WriteSeason(writer);
Assert.That(writer.ToString(), Is.EqualTo("Winter"));
}
}
}
This will allow the test to run, but will limit you to testing a single path through the code. It's also flaky, because your test depends on a magic string in a separate file.
using System.IO;
namespace Demo.Test
{
[TestFixture]
public class SeasonWriterTests
{
[Test]
public void WriteSeasonExpectWinter()
{
var writer = new StringWriter();
var target = new SeasonWriter();
target.WriteSeason(writer);
Assert.That(writer.ToString(), Is.EqualTo("Winter"));
}
}
}
Pass the configuration setting in
This solution is a refactor of the WriteSeason method, so it takes the config setting in as a string. This sounds like a clean fix, with benefits
- the responsibility for getting that value has been handed off to the user of the method
- the code is easier to test
However, there are downsides
- the refactor is an untested change
- there could be many users of this method, and each one would be responsible for getting the month value
- the exception type in the WriteSeason method is no longer correct
Set the config value
The easiest way to fake an AppSettings value is just to set it in your test class. ConfigurationManager.AppSettings is just a NameValueCollection, and items can be written as well as read:
using NUnit.Framework;
using System.IO;
using System.Configuration;
namespace Demo.Test
{
[TestFixture]
public class SeasonWriterTests
{
[TestCase("January", "Winter")]
[TestCase("February", "Winter")]
[TestCase("March", "Spring")]
[TestCase("April", "Spring")]
[TestCase("May", "Spring")]
[TestCase("June", "Summer")]
[TestCase("July", "Summer")]
[TestCase("August", "Summer")]
[TestCase("September", "Autumn")]
[TestCase("October", "Autumn")]
[TestCase("November", "Autumn")]
[TestCase("December", "Winter")]
public void WriteSeasonExpectCorrectSeasonForMonth(string month, string expectedSeason)
{
ConfigurationManager.AppSettings["Month"] = month;
var writer = new StringWriter();
var target = new SeasonWriter();
target.WriteSeason(writer);
Assert.That(writer.ToString(), Is.EqualTo(expectedSeason));
}
}
}
using System.IO;
using System.Configuration;
namespace Demo.Test
{
[TestFixture]
public class SeasonWriterTests
{
[TestCase("January", "Winter")]
[TestCase("February", "Winter")]
[TestCase("March", "Spring")]
[TestCase("April", "Spring")]
[TestCase("May", "Spring")]
[TestCase("June", "Summer")]
[TestCase("July", "Summer")]
[TestCase("August", "Summer")]
[TestCase("September", "Autumn")]
[TestCase("October", "Autumn")]
[TestCase("November", "Autumn")]
[TestCase("December", "Winter")]
public void WriteSeasonExpectCorrectSeasonForMonth(string month, string expectedSeason)
{
ConfigurationManager.AppSettings["Month"] = month;
var writer = new StringWriter();
var target = new SeasonWriter();
target.WriteSeason(writer);
Assert.That(writer.ToString(), Is.EqualTo(expectedSeason));
}
}
}