I'm a quite new person for C# and unit tests. I need to write a unit tests for a switch statement and I have to admit that I already have been through many of pages trying to find something. Does anyone could give me any tip how to create it? please My code below:
public static Message create(String body)
{
Message result = null;
switch (body.ToArray()[0])
{
case 'S':
//checking the regex for a sms message
Match matchS = Regex.Match(body, @"[S][0-9]{9}\+[0-9]{12}");
if (matchS.Success)
{
MessageBox.Show("This is SMS message");
//if the regex will match a sms_message class will be started
result = new Sms_Message(body.Substring(1));
}
//if the regex doesn't match the message should be displayed
else throw new Exception("I don't like content!!!!!!!!!!");
break;
case 'T':
//checking the regex for a tweet message
Match matchT = Regex.Match(body, @"[T][0-9]{9}");
if (matchT.Success)
{
MessageBox.Show("This is the Tweet message");
//if the regex match the message should be displayed and the class Tweet will be started
result = new Tweet(body.Substring(1));
}
break;
case 'E':
//checking the regex for a email message
Match matchE = Regex.Match(body, @"[E][0-9]{9}");
if (matchE.Success)
{
//checking the content of the message by using function 'BodyIsSir'
if (BodyIsSir(body))
//if function return true the SIREmail class will be started
result = new SIREmail(body.Substring(1));
else
//if function return false the StandardEmail class will be started
result = new StandardEmail(body.Substring(1));
}
//when regex will not match the text message will be displayed
else throw new Exception("I don't like the content");
break;
default:
//when the content of the email message will not match
throw new Exception("I dont like first letter!");
}
return result;
}
private static bool BodyIsSir(string body)
{
//checking the body of email message if this contain regex for checking the sort code
Match matchSIR = Regex.Match(body, @"[0-9]{2}[-][0-9]{2}[-][0-9]");
if (matchSIR.Success)
return true;
else
return false;
}
I need to write a unit tests for a switch statement
No you don't, because that's not a unit.
The unit is:
public static Message create(String body) (That is to say, what you can see of the code, from the outside).body. (One of the advantages of methods that aren't affected by state but only by inputs is that it therefore makes testing easier).You write your unit tests to:
switch was replaced by some other mechanism, the tests should not change, but you aren't testing a switch any more).So, you want to write tests that do this. The basic approach is simple enough, you have a bunch of different inputs, a bunch of different expected outputs or exceptions, and you write a test to check on it.
You don't say what testing framework you are using. I recommend XUnit, but NUnit and MSUnit are also good.
Keep unit tests small, testing just one thing*, though perhaps examining a few features of those things†. For example:
[Fact]
public void SMSMessage()
{
Message msg = YourClass.create("S123456789+123456789012");
Assert.IsType<Sms_Message>(msg);
Assert.Equal("123456789+123456789012", msg.Body);
}
(In NUnit or MSUnit [Fact] would be [Test], .IsType would be .IsInstanceOfType and .Equal would be .AreEqual)
Checking exceptions is just as important as checking correct uses. One of the reasons for preferring XUnit over NUnit or MSUnit is that while XUnit and MSUnit have an [ExpectedException] attribute that defines the type of exception expected, XUnit has a method that allows for better checking the precise call that throws the exception (so tests can't indicate success by throwing the right exception at the wrong time) and allows for examining the exception thrown:
[Fact]
public void InvalidSMS()
{
Exception ex = Assert.Throws<Exception>(() => YourClass.create("S12345"));
Assert.Equal("I don't like content!!!!!!!!!!", ex.Description);
}
You can also carry out tests over a large range of data:
public static IEnumerable<object[]> ValidSMSMessages()
{
yield return new object[] { "123456789-123456789012" }
yield return new object[] { "123456912-123456789012" }
yield return new object[] { "123672389-123456789012" }
yield return new object[] { "121233789-123456789012" }
yield return new object[] { "123456789-123456781212" }
yield return new object[] { "123456789-121216789012" }
// One could probably think of better examples here based on a mixture of realistic and edge-case uses.
}
[Theory]
[MemberData("ValidSMSMessages")]
public void SMSMessages(string smsBody)
{
Message msg = YourClass.create("S" + smsBody);
Assert.IsType<Sms_Message>(msg);
Assert.Equal(smsBody, msg.Body);
}
Always try to think of the edge-cases. For example, if null or an empty string can be passed to a method then you should test doing so, and that one either gets the correct result (if doing so is valid) or the correct exception (if doing so is invalid). (It's a bonus here that Assert.Throws<> when the type is either ArgumentException or derived from it has a form that takes an argument with the expected parameter name)
[Fact]
public void NullBody()
{
Assert.Throws<ArgumentNullException>("body", () => YourClass.create(null));
}
[Fact]
public void EmptyBody()
{
Assert.Throws<ArgumentException>("body", () => YourClass.create(""));
}
Note that the code in your answer fails both these tests. Hurray! Our testing has found two bugs to fix.
(It's not clear to me whether the fact that input of "T" would return null is a bug or by design. This is one reason why I prefer to return out of switch blocks immediately rather than assign within them and then return at the end; if this approach was taken you would have had to return null explicitly if you wanted it, or a compiler error. So it would either be obvious to someone reading the code that returning null was correct, or if incorrect it would have been fixed).
What we can't find as easily with unit tests are design flaws. In the code in question in there are the following design flaws:
Exception rather than a more derived type more specific to the case.MessageBox.Show() from within a factory method.ToArray() which wastes time and memory allocating a char[] just to access [0] on that array, when replacing body.ToArray()[0] with body[0] would have the same effect more efficiently.However:
ToArray() call some time afterwards. With unit testing in place we can run the unit tests again after taking it out. If our performance improvement somehow broke something we'd know (it won't, but if we could be right about everything like that all the time we wouldn't need any testing at all…). Conversely, while the tests all running doesn't prove we haven't broken anything, they can certainly increase our confidence that we haven't.Use coverage tools as a guide, not a crutch. Don't look at coverage reports when you first write your tests. Then when you do, find the code paths that your tests aren't covering, think about what sort of situations would involve them, and then add tests for those cases and similar cases without looking at the coverage while you improve it. This way coverage really does guide you to write better tests, but if you are constantly looking at the coverage it can be easy to fall into the trap of writing tests that get "perfect" coverage without actually testing much. Tests with poor coverage that cover a wide variety of cases are better than tests with 100% line and branch coverage that don't really exercise the possible permutations. (Of course, its also possible that a branch isn't covered because it's impossible and then you can remove the dead code and/or replace a branch with an assertion that the branch is unreachable).
*It's possible that somebody reading this may have seen unit tests written by me on open-source projects that broke this rule. I got better.
†An example of testing lots of features that are valid is that if perhaps a method was supposed to return an IList<T> that was read-only, it's reasonable to test all of the features of a read-only IList<T> in the same test, as they're all aspects of the same well-defined concept. E.g.:
[Fact]
public void ResultIsReadonly()
{
IList<int> list = SomeMethodThatReturnsAReadonlyList();
Assert.True(list.IsReadonly);
Assert.Throws<NotSupportedException>(() => list.Add(5));
Assert.Throws<NotSupportedException>(() => list.Clear());
Assert.Throws<NotSupportedException>(() => list.Insert(0, 1));
Assert.Throws<NotSupportedException>(() => list.Remove(1));
Assert.Throws<NotSupportedException>(() => list.RemoveAt(0));
Assert.Throws<NotSupportedException>(() => list[0] = 1);
}
While this has seven assertions, they are all necessary to test the same feature of the result. Conversely, if we were more concerned about a class that implemented a read-only list than a method that returns one, we should probably be thinking about those features severally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With