switch语句C#的测试用例

卢卡斯

我是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;                                                                   
}
乔恩·汉纳

我需要为switch语句编写单元测试

不,您不会,因为那不是一个单位。

单位是:

  1. public static Message create(String body) (也就是说,您可以从外部看到代码)。
  2. 影响该方法的行为的任何状态,使得它可能对同一方法有所不同body(不受状态影响而仅受输入影响的方法的优点之一是因此使测试更加容易)。
  3. 无论该文档是写下来的还是仅在您的脑海中记录,它应该做什么的行为都已记录在案。(这只是一件坏事,但在某些情况下,测试本身可以用作预期行为的低级文档)。

您将单元测试编写为:

  1. 也许到达正确的起点(如果您在代码之前编写测试,这通常是一个好方法)。
  2. 确保您自己和其他人该代码正常工作。
  3. 捕获由以后的更改引起的错误,意外地破坏了这段代码。(特别是在涉及到您的问题时。如果已将其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跳出switch块而不是在块中最后分配对象的原因之一return;如果采用了这种方法,则是您如果需要,则必须return null明确指出,否则将产生编译器错误。因此,对于阅读代码的人来说,显而易见的是返回null是正确的,或者如果返回错误,则可以将其修复)。

我们无法通过单元测试轻松找到设计缺陷。在所讨论的代码中,存在以下设计缺陷:

  1. 使用不遵循.NET约定的名称(小写方法名称,方法名称中使用下划线)。
  2. 抛出Exception而不是更具体的情况派生出更多类型。
  3. 不将业务和显示逻辑分开,而是MessageBox.Show()从工厂方法中进行调用
  4. 电话ToArray()这浪费时间和内存分配char[]只是访问[0]该阵列上,更换时body.ToArray()[0]body[0]会更有效地产生同样的效果。

然而:

  1. 考虑如何测试方法会迫使您考虑该方法应如何工作,这会使您未曾注意到的设计缺陷更加明显。
  2. 单元测试使改进更加安全。假设我们只是在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);
}

尽管有七个断言,但它们都是测试结果的相同特征所必需的。相反,如果我们更关心实现只读列表的类而不是返回一个列表的方法,则我们可能应该分别考虑这些功能。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章