【问题标题】:Line of Code in my Private Method is never Covered我的私有方法中的代码行从未被覆盖
【发布时间】:2014-06-08 05:53:54
【问题描述】:

下面是动作方法的一段代码:

private static IEnumerable<SelectListItem> GetProductTypes()
{
    System.Text.RegularExpressions.Regex filter = new System.Text.RegularExpressions.Regex("Prodcut1|Prodcut2|Prodcut3");
    var prodtypes = from ProdType e in Enum.GetValues(typeof(ProdType))
                    where filter.IsMatch(e.ToString())
                    select new { Id = (int)e, Name = e.ToString() };

    if (prodtypes  != null)
    {
        return prodtypes .Select(x => new SelectListItem
               {
                   Value = x.Id.ToString(),
                   Text = x.Name
               });
    }
    return new List<SelectListItem>();
}

其中:ProdType 是枚举。其中包含一些产品类型及其值。

我已经为该方法编写了单元测试:-

[TestMethod]
public void GetProductTypes_Test()
{
    //Arrange        
    PrivateType pvtType = new PrivateType(typeof(ProductController));
    //Act
    var actual = (IEnumerable<SelectListItem>)pvtType.InvokeStatic("GetProductTypes");

    //Assert
    Assert.IsNotNull(actual);
    Assert.IsInstanceOfType(actual, typeof(IEnumerable<SelectListItem>));
    Assert.AreEqual(3, actual.ToList().Count); //here : 3 = Product1,Product2,Product3
}

但是当我通过代码覆盖率选项检查它的代码覆盖率时,它没有覆盖下面的代码行:-

return new List<SelectListItem>();

谁能建议我在这里做错了什么?是的,这是我正在测试的私有方法。因为我想测试所有私有方法。

【问题讨论】:

  • 该行永远不会被覆盖,因为Select 本身永远不会返回null
  • 许多开发人员会不同意您对私有方法的测试。它们是内部实现,应该可以随意更改。您应该只测试应用程序的公开可见表面。在此过程中,私有方法将通过其效果进行间接测试。
  • 将来你会因为测试这样的私有方法而遭受巨大的痛苦,特别是如果你曾经对它们的名字进行一些重构......

标签: c# unit-testing asp.net-mvc-4 moq private-members


【解决方案1】:

正如 Patryk 已经说过的:prodtypes 永远不会是 null
要获得 100% 的覆盖率,您可以将该方法调整为以下之一(仅第二部分 - 第一部分保持不变):

private static IEnumerable<SelectListItem> GetProductTypes()
{
    //select prodtypes
    if (prodtypes != null && prodtypes.Count() > 0)
    {
        return prodtypes.Select(x => new SelectListItem
               {
                    Value = x.Id.ToString(),
                    Text = x.Name
               });
    }
    return new List<SelectListItem>();
}

或更短且性能更高的,因为它不应该是null

private static IEnumerable<SelectListItem> GetProductTypes()
{
    //select prodtypes
    return prodtypes.Select(x => new SelectListItem
       {
            Value = x.Id.ToString(),
            Text = x.Name
       });
}

但是
正如约翰所说:在大多数情况下,您不应该对私有方法进行单元测试,因为它们应该被一些公共接口/方法所覆盖。如果没有涵盖,请考虑该方法 - 也许甚至不需要 - 我已经发生过几次了。
但我也添加了“在大多数情况下”,因为在某些情况下测试私有方法可能是有意义的。在我的情况下,例如我的硬件有一些错误处理方法,这些方法在正常单元测试期间没有附加,所以我直接调用“私有错误处理程序”来检查它是否行为正确。但是你的方法看起来不像这样……

更新
正如 Episodex 所建议的,该方法的更好解决方案:

private static IEnumerable<SelectListItem> GetProductTypes()
{
    var filter = new System.Text.RegularExpressions.Regex("Prodcut1|Prodcut2|Prodcut3");
    return from ProdType e in Enum.GetValues(typeof(ProdType))
           where filter.IsMatch(e.ToString())
           select new SelectListItem 
               {
                   Value = ((int)e).ToString(), 
                   Text = e.ToString() 
                };
}

【讨论】:

  • 如果prodtypes 永远不会为空,那么在您的第一个示例中检查null 是没有意义的。而不是检查.Count() &gt; 0,您应该使用.Any() 以获得更好的性能。但无论如何,您的第二个示例似乎是这里唯一合理的解决方案。事实上,这个选择可以替换 linq 查询中使用的那个(为什么选择两次?)。
  • @Chrfin,正如约翰所说,私有方法不应该测试,它应该通过其不同的行为在公共方法中进行测试。但是在我的应用程序中有很多私有方法,因此代码覆盖率我的控制器正在起作用。这就是我想测试私有方法的原因。我也在公共方法单元测试中测试私有方法的不同行为。 >>所以你是说我应该使用第二部分的方法?但是如果 prodtypes 为 null 则可能是错误的原因,那么它会抛出异常。
  • @Pawan prodtypes 不能为空,因为如果不选择任何内容,LINQ 将返回空列表,而不是空。
  • @Pawan:然后您应该以各种不同的方式测试控制器操作,以便全部调用私有方法,但不要直接调用它们。如果您无法通过控制器调用所有私有方法,用户应该如何调用它们?正如已经说过的:prodtypes 不能是null,因为选择永远不会返回这个。如果没有项目匹配,它将返回一个空的IEnumerable,其中包含 0 个项目,但不是 null...
猜你喜欢
  • 2017-06-29
  • 2014-05-28
  • 2011-01-01
  • 2013-01-31
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多