在sonarqube .NET代码覆盖率的差异 [英] Code coverage differences in sonarqube .Net

查看:913
本文介绍了在sonarqube .NET代码覆盖率的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现SonarQube 5.3这样一个场景,被报道从Visual Studio代码覆盖分析代码覆盖不同的值。



下面是一个小的复制,使用的MSTest框架。



我无法确定,如果有什么错的,我们在做什么,或者如果一个应用程序是错误的。



待测物

  [Serializable接口] 
公共类文档:IEquatable<文件> ;
{
众长标识{搞定;组; }
公共字符串名称{;组; }
众长DocumentHandle {搞定;组; }
众长BatchId {搞定;组; }
公共字符串BatchName {搞定;组; }
公共字符串RepositoryName {搞定;组; }
众长DocumentTypeId {搞定;组; }
公共字符串DocumentTypeName {搞定;组; }
公众诠释的SequenceNumber {搞定;组; }
公共字符串LoanNumber {搞定;组; }
公共BOOL IsJunked {搞定;组; }
公众的DateTime ArrivalDate {搞定;组; }

公共布尔等于(其他文件)
{
如果(的ReferenceEquals(其他,NULL))
{
返回FALSE;
}

如果(ID = other.Id!)
{
返回FALSE;
}

如果
{
返回假(string.Equals(名称,other.Name)!);
}

如果(DocumentHandle = other.DocumentHandle!)
{
返回FALSE;
}

如果(BatchId = other.BatchId!)
{
返回FALSE;
}

如果
{
返回假(string.Equals(BatchName,other.BatchName)!);
}

如果
{
返回假(string.Equals(RepositoryName,other.RepositoryName)!);
}

如果(DocumentTypeId = other.DocumentTypeId!)
{
返回false;
}

如果
{
返回假(string.Equals(DocumentTypeName,other.DocumentTypeName)!);
}

如果(的SequenceNumber = other.SequenceNumber!)
{
返回FALSE;
}

如果
{
返回假(string.Equals(LoanNumber,other.LoanNumber)!);
}

如果(IsJunked = other.IsJunked!)
{
返回false;
}

如果(ArrivalDate = other.ArrivalDate!)
{
返回false;
}

返回真;
}

公众覆盖布尔等于(obj对象)
{
如果(的ReferenceEquals(NULL,OBJ))
{
返回假;
}

如果(的ReferenceEquals(这一点,OBJ))
{
返回真;
}

返回等于((文件)目标文件);
}

公共静态布尔运算符==(文档文档1,文档文件2)
{
返回的ReferenceEquals(文档1,NULL)?的ReferenceEquals(文件2,空):document1.Equals(文件2);
}

公共静态布尔运算符=(文档文档1,文档文件2)
{
返回(文档1 ==文件2);!
}

公共覆盖INT的GetHashCode()
{
//禁用ReSharper的一次BaseObjectGetHashCodeCallInGetHashCode
//这样做是为了抑制约需要的信息重写GetHashCode
//因为这个类没有只读属性没有办法提供更好的哈希码
返回base.GetHashCode();
}
}



该码具有以下测试:



  [TestClass中] 
[ExcludeFromCodeCoverage]
公共类DocumentTests
{
私人文件defaultDocument;
私人文件alteredDocument;

[TestInitialize]
公共无效设置()
{
defaultDocument =新文件
{
ID = 1,
NAME =Growlithe,
DocumentHandle = 2,
BatchId = 3,
BatchName =六尾,
RepositoryName =Pancham,
DocumentTypeId = 4,
DocumentTypeName =Skrelp,
的SequenceNumber = 5,
LoanNumber =Zorua,
IsJunked =真,
ArrivalDate =新日期时间(1,1,1 )
};

alteredDocument =新文件
{
ID = 1,
NAME =Growlithe,
DocumentHandle = 2,
BatchId = 3 ,
BatchName =六尾,
RepositoryName =Pancham,
DocumentTypeId = 4,
DocumentTypeName =Skrelp,
的SequenceNumber = 5,
LoanNumber =Zorua,
IsJunked = TRUE,
ArrivalDate =新日期时间(1,1,1)
};
}

[TestMethod的]
公共无效ToStringMethod_DocumentPOCO_ConvertObjectToString()
{
//安排
VAR expectedStringDocument =文档+环境。 NEWLINE +
\tId:101+ Environment.NewLine +
\tName:测试名+ Environment.NewLine +
\tDocumentHandle:500万支+ Environment.NewLine +
\tBatchId:5亿+ Environment.NewLine +
\tBatchName:TestBatchName+ Environment.NewLine +
\tRepositoryName:TestRepositoryName+ Environment.NewLine +
\tDocumentTypeId:500万支+ Environment.NewLine +
\tDocumentTypeName:TestDocumentTypeName+ Environment.NewLine +
\tSequenceNumber:101+ Environment.NewLine +
\tLoanNumber:TestLoanNumber+ Environment.NewLine +
\tIsJunked:假+ Environment.NewLine +
\tArrivalDate:+ DateTime.Now + Environment.NewLine;

alteredDocument =新文件
{
n = 101,
NAME =测试名,
DocumentHandle = 5000000
BatchId = 5亿,
BatchName =TestBatchName,
RepositoryName =TestRepositoryName,
DocumentTypeId = 5000000
DocumentTypeName =TestDocumentTypeName,
的SequenceNumber = 101,
LoanNumber =TestLoanNumber,
IsJunked =假,
ArrivalDate = DateTime.Now
};

//法案
VAR processedDocumentObj = StringUtility.StringUtility.ConvertToString(alteredDocument);

//断言
Assert.IsTrue(processedDocumentObj.Equals(expectedStringDocument));
}

[TestMethod的]
公共无效EqualsReturnsTrueForEquivalentDocument()
{
Assert.IsTrue(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForNullDocument()
{
alteredDocument = NULL;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentId()
{
alteredDocument.Id = 9;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentName()
{
alteredDocument.Name =Arcanine;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentDocumentHandle()
{
alteredDocument.DocumentHandle = 9;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentBatchId()
{
alteredDocument.BatchId = 9;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentBatchName()
{
alteredDocument.BatchName =Ninetails;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentRepositoryName()
{
alteredDocument.RepositoryName =Pangoro;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentDocumentTypeId()
{
alteredDocument.DocumentTypeId = 9;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentDocumentTypeName()
{
alteredDocument.DocumentTypeName =Dragalge;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentSequenceNumber()
{
alteredDocument.SequenceNumber = 9;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentLoanNumber()
{
alteredDocument.LoanNumber =Zoroark;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentIsJunked()
{
alteredDocument.IsJunked = FALSE;

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsReturnsFalseForDifferentArrivalDate()
{
alteredDocument.ArrivalDate =新日期时间(2,2,2);

Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}

[TestMethod的]
公共无效EqualsOperatorWorksForNonNullValues()
{
Assert.IsTrue(defaultDocument == alteredDocument);
}

[TestMethod的]
公共无效NotEqualsOperatorWorksForNonNullValues()
{
alteredDocument = NULL;

Assert.IsTrue(!defaultDocument = alteredDocument);
}

[TestMethod的]
公共无效EqualsOperatorReturnsFalseForNullDotNonNull()
{
alteredDocument = NULL;

Assert.IsFalse(alteredDocument == defaultDocument);
}

[TestMethod的]
公共无效EqualsOperatorReturnsFalseForNonNullDotNull()
{
alteredDocument = NULL;

Assert.IsFalse(defaultDocument == alteredDocument);
}

[TestMethod的]
公共无效EqualsOperatorReturnsTrueForNullDotNull()
{
alteredDocument = NULL;
defaultDocument = NULL;

Assert.IsTrue(defaultDocument == alteredDocument);
}
}






Visual Studio的显示比例为:90.10%



SonarQube显示比例为:40.00%






声纳似乎没有考虑



术后早期return语句

 如果(的ReferenceEquals(其他,NULL))
{
返回FALSE;
}



方法:公共布尔等于(其他文件)



我调试的测试,以验证线被击中。


解决方案

这可能是行/分支覆盖之间的差异:
什么是声纳



代码覆盖率和覆盖线之间的区别

.. 。或白色间距/换行



您可以找到SonarQube的度量这里介绍页面公式:的 http://docs.sonarqube.org/display/SONAR/Metric+definitions#Metricdefinitions-Tests



覆盖率=(CT + CF + LC)/(2 * b + EL)



其中,


$该评估为假至少一次
LC分支 - - 线覆盖(lines_to_cover - uncovered_lines),该评价为真至少一次
的CF的分支 -

CT >

b - 分支总数(2 * b = conditions_to_cover)
EL - 可执行行总数(lines_to_cover)


I found a scenario with SonarQube 5.3 where different values are being reported in code coverage from Visual Studio code coverage analysis.

Here is a small reproduction, using the MSTest framework.

I'm not able to determine if there is something wrong in what we are doing or if one of the applications is wrong.

The object being tested

[Serializable]
public class Document : IEquatable<Document>
{
    public long Id { get; set; }
    public string Name { get; set; }
    public long DocumentHandle { get; set; }
    public long BatchId { get; set; }
    public string BatchName { get; set; }
    public string RepositoryName { get; set; }
    public long DocumentTypeId { get; set; }
    public string DocumentTypeName { get; set; }
    public int SequenceNumber { get; set; }
    public string LoanNumber { get; set; }
    public bool IsJunked { get; set; }
    public DateTime ArrivalDate { get; set; }

    public bool Equals(Document other)
    {
        if (ReferenceEquals(other, null))
        {
            return false;
        }

        if (Id != other.Id)
        {
            return false;
        }

        if (!string.Equals(Name, other.Name))
        {
            return false;
        }

        if (DocumentHandle != other.DocumentHandle)
        {
            return false;
        }

        if (BatchId != other.BatchId)
        {
            return false;
        }

        if (!string.Equals(BatchName, other.BatchName))
        {
            return false;
        }

        if (!string.Equals(RepositoryName, other.RepositoryName))
        {
            return false;
        }

        if (DocumentTypeId != other.DocumentTypeId)
        {
            return false;
        }

        if (!string.Equals(DocumentTypeName, other.DocumentTypeName))
        {
            return false;
        }

        if (SequenceNumber != other.SequenceNumber)
        {
            return false;
        }

        if (!string.Equals(LoanNumber, other.LoanNumber))
        {
            return false;
        }

        if (IsJunked != other.IsJunked)
        {
            return false;
        }

        if (ArrivalDate != other.ArrivalDate)
        {
            return false;
        }

        return true;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        return Equals((Document) obj);
    }

    public static bool operator == (Document document1, Document document2)
    {
        return ReferenceEquals(document1, null) ? ReferenceEquals(document2, null) : document1.Equals(document2);
    }

    public static bool operator != (Document document1, Document document2)
    {
        return !(document1 == document2);
    }

    public override int GetHashCode()
    {
        // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
        // This was done to suppress the messages about needing to override GetHashCode
        // Because this class has no ReadOnly properties there is no way to provide a better hashcode
        return base.GetHashCode();
    }
}

The code has the following tests:

[TestClass]
[ExcludeFromCodeCoverage]
public class DocumentTests
{
    private Document defaultDocument;
    private Document alteredDocument;

    [TestInitialize]
    public void Setup()
    {
        defaultDocument = new Document
        {
            Id = 1,
            Name = "Growlithe",
            DocumentHandle = 2,
            BatchId = 3,
            BatchName = "Vulpix",
            RepositoryName = "Pancham",
            DocumentTypeId = 4,
            DocumentTypeName = "Skrelp",
            SequenceNumber = 5,
            LoanNumber = "Zorua",
            IsJunked = true,
            ArrivalDate = new DateTime(1, 1, 1)
        };

        alteredDocument = new Document
        {
            Id = 1,
            Name = "Growlithe",
            DocumentHandle = 2,
            BatchId = 3,
            BatchName = "Vulpix",
            RepositoryName = "Pancham",
            DocumentTypeId = 4,
            DocumentTypeName = "Skrelp",
            SequenceNumber = 5,
            LoanNumber = "Zorua",
            IsJunked = true,
            ArrivalDate = new DateTime(1, 1, 1)
        };
    }

    [TestMethod]
    public void ToStringMethod_DocumentPOCO_ConvertObjectToString()
    {
        // Arrange
        var expectedStringDocument = "Document" + Environment.NewLine +
                                "\tId: 101" + Environment.NewLine +
                                "\tName: TestName" + Environment.NewLine +
                                "\tDocumentHandle: 5000000" + Environment.NewLine +
                                "\tBatchId: 500000000" + Environment.NewLine +
                                "\tBatchName: TestBatchName" + Environment.NewLine +
                                "\tRepositoryName: TestRepositoryName" + Environment.NewLine +
                                "\tDocumentTypeId: 5000000" + Environment.NewLine +
                                "\tDocumentTypeName: TestDocumentTypeName" + Environment.NewLine +
                                "\tSequenceNumber: 101" + Environment.NewLine +
                                "\tLoanNumber: TestLoanNumber" + Environment.NewLine +
                                "\tIsJunked: False" + Environment.NewLine +
                                "\tArrivalDate: " + DateTime.Now + Environment.NewLine;

        alteredDocument = new Document
        {
            Id = 101,
            Name = "TestName",
            DocumentHandle = 5000000,
            BatchId = 500000000,
            BatchName = "TestBatchName",
            RepositoryName = "TestRepositoryName",
            DocumentTypeId = 5000000,
            DocumentTypeName = "TestDocumentTypeName",
            SequenceNumber = 101,
            LoanNumber = "TestLoanNumber",
            IsJunked = false,
            ArrivalDate = DateTime.Now
        };

        // Act
        var processedDocumentObj = StringUtility.StringUtility.ConvertToString(alteredDocument);

        // Assert
        Assert.IsTrue(processedDocumentObj.Equals(expectedStringDocument));
    }

    [TestMethod]
    public void EqualsReturnsTrueForEquivalentDocument()
    {
        Assert.IsTrue(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForNullDocument()
    {
        alteredDocument = null;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentId()
    {
        alteredDocument.Id = 9;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentName()
    {
        alteredDocument.Name = "Arcanine";

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentDocumentHandle()
    {
        alteredDocument.DocumentHandle = 9;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentBatchId()
    {
        alteredDocument.BatchId = 9;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentBatchName()
    {
        alteredDocument.BatchName = "Ninetails";

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentRepositoryName()
    {
        alteredDocument.RepositoryName = "Pangoro";

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentDocumentTypeId()
    {
        alteredDocument.DocumentTypeId = 9;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentDocumentTypeName()
    {
        alteredDocument.DocumentTypeName = "Dragalge";

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentSequenceNumber()
    {
        alteredDocument.SequenceNumber = 9;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentLoanNumber()
    {
        alteredDocument.LoanNumber = "Zoroark";

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentIsJunked()
    {
        alteredDocument.IsJunked = false;

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsReturnsFalseForDifferentArrivalDate()
    {
        alteredDocument.ArrivalDate = new DateTime(2, 2, 2);

        Assert.IsFalse(defaultDocument.Equals(alteredDocument));
    }

    [TestMethod]
    public void EqualsOperatorWorksForNonNullValues()
    {
        Assert.IsTrue(defaultDocument == alteredDocument);
    }

    [TestMethod]
    public void NotEqualsOperatorWorksForNonNullValues()
    {
        alteredDocument = null;

        Assert.IsTrue(defaultDocument != alteredDocument);
    }

    [TestMethod]
    public void EqualsOperatorReturnsFalseForNullDotNonNull()
    {
        alteredDocument = null;

        Assert.IsFalse(alteredDocument == defaultDocument);
    }

    [TestMethod]
    public void EqualsOperatorReturnsFalseForNonNullDotNull()
    {
        alteredDocument = null;

        Assert.IsFalse(defaultDocument == alteredDocument);
    }

    [TestMethod]
    public void EqualsOperatorReturnsTrueForNullDotNull()
    {
        alteredDocument = null;
        defaultDocument = null;

        Assert.IsTrue(defaultDocument == alteredDocument);
    }
}


Visual Studio shows the percentage as: 90.10%

SonarQube shows the percentage as: 40.00%


Sonar doesn't appear to consider the early return statements after the

if (ReferenceEquals(other, null))
{
  return false;
}

of the method: public bool Equals(Document other)

I have debugged the tests to validate the lines are hit.

解决方案

It may be a difference between line/branch coverage: What is the difference between code coverage and line coverage in sonar

... or white spacing/line wrapping.

You can find the formula for SonarQube's metric description page here: http://docs.sonarqube.org/display/SONAR/Metric+definitions#Metricdefinitions-Tests

coverage = (CT + CF + LC)/(2*B + EL)

where

CT - branches that evaluated to "true" at least once CF - branches that evaluated to "false" at least once LC - lines covered (lines_to_cover - uncovered_lines)

B - total number of branches (2*B = conditions_to_cover) EL - total number of executable lines (lines_to_cover)

这篇关于在sonarqube .NET代码覆盖率的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆