Luk*_*kas 2 c# unit-testing switch-statement
我是C#和单元测试的新手。我需要为switch语句编写一个单元测试,并且我不得不承认我已经经历了很多尝试寻找内容的页面。有谁能给我一些如何创建的提示?请在下面我的代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
我需要为switch语句编写单元测试
不,您不会,因为那不是单位。
单位是:
public static Message create(String body) (也就是说,您可以从外部看到代码)。body。(不受状态影响而仅受输入影响的方法的优点之一是因此使测试更加容易)。您将单元测试编写为:
switch已用其他某种机制替换了,则测试不应更改,但您将不再进行switch任何测试)。因此,您想要编写执行此操作的测试。基本方法非常简单,您有一堆不同的输入,一堆不同的预期输出或异常,并编写了一个测试来对其进行检查。
您没有说您正在使用什么测试框架。我推荐的xUnit,但NUnit的和MSUnit都还不错。
保持单元测试较小,仅测试一件事*,尽管也许要检查这些东西的一些功能†。例如:
[Fact]
public void SMSMessage()
{
Message msg = YourClass.create("S123456789+123456789012");
Assert.IsType<Sms_Message>(msg);
Assert.Equal("123456789+123456789012", msg.Body);
}
Run Code Online (Sandbox Code Playgroud)
(在NUnit或MSUnit [Fact]中[Test],.IsType将会是.IsInstanceOfType,.Equal将会是.AreEqual)
检查异常与检查正确使用一样重要。选择XUnit而不是NUnit或MSUnit的原因之一是,虽然XUnit和MSUnit具有[ExpectedException]定义期望的异常类型的属性,但XUnit 的方法可以更好地检查引发异常的精确调用(因此测试不能通过在错误的时间抛出正确的异常来表示成功),并允许检查抛出的异常:
[Fact]
public void InvalidSMS()
{
Exception ex = Assert.Throws<Exception>(() => YourClass.create("S12345"));
Assert.Equal("I don't like content!!!!!!!!!!", ex.Description);
}
Run Code Online (Sandbox Code Playgroud)
您还可以对大量数据进行测试:
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);
}
Run Code Online (Sandbox Code Playgroud)
始终尝试考虑边缘情况。例如,如果null可以将一个或空字符串传递给一个方法,则您应该对此进行测试,并获得正确的结果(如果这样做是有效的)或正确的异常(如果这样做是无效的)。(这是一个额外的好处,Assert.Throws<>当类型是类型ArgumentException或从类型派生时,它具有接受带有期望参数名称的参数的形式)
[Fact]
public void NullBody()
{
Assert.Throws<ArgumentNullException>("body", () => YourClass.create(null));
}
[Fact]
public void EmptyBody()
{
Assert.Throws<ArgumentException>("body", () => YourClass.create(""));
}
Run Code Online (Sandbox Code Playgroud)
请注意,答案中的代码无法通过这两项测试。欢呼!我们的测试发现了两个要修复的错误。
(对我来说,不清楚输入的"T"返回null是否是错误还是有意为之。这是为什么我宁愿立即return跳出switch块而不是在块中最后分配的原因之一return;如果采用了这种方法,如果需要,则必须return null明确指出,否则会出现编译器错误。因此,对于阅读该代码的人来说,显而易见的是返回null是正确的,或者如果返回的是不正确的,它将被修复)。
我们无法通过单元测试轻松找到设计缺陷。在所讨论的代码中,存在以下设计缺陷:
Exception而不是根据情况更具体的派生类型。MessageBox.Show()从工厂方法中进行调用。ToArray()这浪费时间和内存分配char[]只是访问[0]该阵列上,更换时body.ToArray()[0]用body[0]会更有效地产生同样的效果。然而:
ToArray()事后的某个时间意识到通话的浪费。有了单元测试,我们可以在取出单元测试后再次运行它。如果我们的性能改进以某种方式破坏了我们所知道的(它不会,但是如果我们一直能做到这样的事情,那么我们根本就不需要任何测试……)。相反,尽管所有运行的测试都不能证明我们没有破坏任何东西,但是它们无疑可以增加我们对没有破坏的信心。使用覆盖率工具作为指导,而不是拐杖。初次编写测试时,请勿查看覆盖率报告。然后,当您这样做时,找到测试未涵盖的代码路径,考虑将涉及哪种情况,然后为这些情况和类似情况添加测试无需在改善覆盖范围的情况下查看覆盖范围。这样,覆盖率确实可以指导您编写更好的测试,但是,如果您一直在关注覆盖率,很容易陷入编写测试的陷阱,而这些测试却获得了“完美”的覆盖率,而没有实际进行过多测试。具有较差覆盖率的测试(涵盖各种情况)要比具有100%的行和分支覆盖率的测试更好,而这些测试并没有真正行使可能的排列。(当然,也有可能因为不可能而没有覆盖分支,然后可以删除无效代码和/或用断言分支无法到达的断言替换分支)。
*阅读此书的某人可能已经看到我在违反此规则的开源项目上编写的单元测试。我好了
†测试许多有效功能的示例是,如果也许某个方法应该返回IList<T>只读的,则IList<T>在同一测试中测试只读的所有功能是合理的,因为它们是相同概念的所有方面。例如:
[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);
}
Run Code Online (Sandbox Code Playgroud)
尽管有七个断言,但它们都是测试结果的相同特征所必需的。相反,如果我们更关心实现只读列表的类而不是返回一个方法的类,则我们可能应该分别考虑这些功能。
| 归档时间: |
|
| 查看次数: |
4683 次 |
| 最近记录: |