Code coverage differences in sonarqube .Net

安稳与你 提交于 2019-12-11 09:53:55

问题


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.


回答1:


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)



来源:https://stackoverflow.com/questions/35876546/code-coverage-differences-in-sonarqube-net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!