我是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;
}
答案 0 :(得分:5)
我需要为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);
}
(在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);
}
您还可以对大量数据进行测试:
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);
}
总是试着想一下边缘情况。例如,如果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(""));
}
有一个带有预期参数名称的参数的表格,这是一个奖励。
"T"
请注意,答案中的代码都未通过这些测试。欢呼!我们的测试发现了两个需要修复的错误。
(我不清楚null
的输入是否会返回return
是一个错误还是设计错误。这是我更喜欢{{1}的原因之一} switch
阻止return
阻塞而不是在其中分配,然后在return null
结束;如果采用这种方法,如果你需要它,你将不得不null
显式,或编译器因此,对于阅读代码的人来说,返回Exception
是正确的,或者如果不正确,它会被修复,这是显而易见的。
我们无法通过单元测试轻松找到设计缺陷。在有问题的代码中存在以下设计缺陷:
MessageBox.Show()
而不是更具特色的派生类型。ToArray()
。char[]
替换[0]
时,调用body.ToArray()[0]
浪费时间和内存分配body[0]
只是为了访问该数组上的ToArray()
效果更高。然而:
IList<T>
一段时间。通过单元测试,我们可以在取出后再次进行单元测试。如果我们的性能改进在某种程度上打破了我们所知道的东西(它不会,但如果我们对所有事情都是正确的,那么我们根本不需要任何测试......)。相反,虽然所有运行的测试都没有证明我们没有破坏任何东西,但它们肯定会增加我们对自己的信心。使用覆盖工具作为指导,而不是拐杖。在第一次编写测试时,请不要查看覆盖率报告。然后,当你这样做时,找到你的测试没有覆盖的代码路径,考虑将涉及它们的那种情况,然后为那些和类似情况添加测试,而不是查看覆盖,而你改进它。这种覆盖方式确实可以指导您编写更好的测试,但是如果您经常查看覆盖范围,那么很容易陷入编写测试的陷阱中,这些测试可以完美地实现&#34;没有实际测试的覆盖范围。覆盖范围广,覆盖范围广泛的测试比100%线路和分支覆盖范围的测试要好,而这些测试并不能真正实现可能的排列。 (当然,也可能没有覆盖分支,因为它是不可能的,然后你可以删除死代码和/或用断言无法访问的断言替换分支。)
*读这篇文章的人可能已经看到我在开源项目中编写的单元测试打破了这一规则。我好多了。
†测试许多有效特征的一个例子是,如果一个方法可能返回一个只读的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);
}
,因为它们是同一个明确定义概念的所有方面。 E.g:
{{1}}
虽然这有七个断言,但它们都是测试结果的相同特征所必需的。相反,如果我们更关心一个实现只读列表的类而不是一个返回一个的方法,我们应该分别考虑这些特性。
答案 1 :(得分:0)
在我看来,你应该为每个案例创建几个测试。
您可以使用不同的方法为不同的情况创建帮助程序接口和类(我称之为IController
)。
IController controller;
case 'S':
controller.CaseS ();
break;
case 'T':
controller.CaseT(body);
break;
case'E':
controller.CaseE(body);
break;
default:
throw new Exception("I dont like first letter!");
进行小型单元测试:如果身体开始于&#39; S&#39;它将使用您的IController
模拟对象调用controller.CaseS()等。
然后进行测试以涵盖IController
的非模拟实现的行为。
或者使用不同的输入进行多次测试:如果身体相等&#34; S ...&#34;如果应该返回此结果。如果身体相等&#39; P ..&#39;它抛出异常
另外,我建议您避免使用MessageBox.Show
dirreclty并使用IMEssageBoxHelper
方法创建Show
并使用它而不是MessageBox.Show()
,因此在测试中您可以将其更改为模拟实现,因此您的测试将在没有消息框的情况下运行。
答案 2 :(得分:0)
如果您想测试可能的情况,可以在测试方法上使用TestCaseSource
属性。这将迭代指定的源并使用每个项调用测试方法。
[TestFixture]
public class MessageTests
{
static Case[] ValidCases = {
new Case ("S...", typeof(Sms_Message)),
new Case ("T...", typeof(Tweet)),
new Case ("E...", typeof(SIREmail)),
new Case ("E...", typeof(StandardEmail))
};
[Test]
[TestCaseSource("ValidCases")]
public void Create_ShouldCreate_WhenValidSource(Case currentCase)
{
// when
Message created = TargetClass.create (currentCase.Body);
// then
Assert.That (created.GetType (), Is.InstanceOf (currentCase.ExpectedResultType));
}
public class Case
{
public Case(string body, Type expectedResultType)
{
Body = body;
ExpectedResultType = expectedResultType;
}
public string Body { get; private set; }
public Type ExpectedResultType { get; private set; }
}
}
答案 3 :(得分:0)
在测试任何方法时,您需要考虑任何客户端在调用方法时将遇到的行为。从本质上讲,这归结为提出一个简单的问题:
通过查看不同的&#34;分支&#34;在您的方法中,您可以计算出您需要测试的不同输入以获得全面覆盖&#34;你的方法。例如,在您的方法中,以S
开头的输入有两种可能的结果:它与正则表达式匹配 - 或者它抛出异常。类似的分析应该产生一组输入,这些输入应该涵盖通过您的方法的每条可能的路径。
必须删除对方法的MessageBox.Show()
依赖关系 - 因为任何自动化测试都将在此步骤停止,并且需要手动干预才能实际从方法中获得任何结果。答案here中的建议很好 - 使用界面消除对此的依赖。