验证虚拟方法(MOQ)

时间:2016-02-01 16:38:36

标签: c# unit-testing moq

所以我是TDD的新手,这是我的问题:

我有一个有几种方法的课。像这样:

public class CompanyService : ICompanyService
{
    public ICompanyRepository companyRepository;
    public CompanyService() : this(new CompanyRepository())
    {                
    }

    public CompanyService(ICompanyRepository repository)
    {
        companyRepository = repository;
    }


    public virtual bool InsertCompany(Company company)
    {   
        return companyRepository.InsertCompany(company);
    }

    public bool InsertCompany(Company company, int total)
    {
        if (AddTotals(total))
        {
            return this.InsertCompany(company);
        }

        return false;
    }


    /// <summary>
    /// Wrapper for the static method at static service
    /// </summary>
    /// <param name="total"></param>
    /// <returns></returns>
    public virtual bool AddTotals(int total)
    {
        return StaticService.AddTotals(total);
    }
}

我的大部分测试都适用于此课程。所以这是我的单元测试:

    [TestMethod]
    public void Test_Unit_AddTotals()
    {
         var service = new CompanyService();
         Assert.IsFalse(service.AddTotals(1));
    }


    [TestMethod]
    public void Test_Unit_InsertCompany_IsExecuted()
    {
        Guid id = GenerateCustomerID();
        var company = new Company { CustomerID = id, CompanyName = "CFN-" + id };
        var mock = new Mock<CompanyService>();
        mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);
        mock.Object.InsertCompany(company, 1);
        mock.Verify(t => t.InsertCompany(It.IsAny<Company>()),Times.Once);
    }

    [TestMethod]
    public void Test_Unit_InsertCompany_IsSuccess()
    {
        Guid id = GenerateCustomerID();
        var company = new Company { CustomerID = id, CompanyName = "CFN-" + id };
        var mock = new Mock<CompanyService>();
        mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);
        mock.Setup(t => t.InsertCompany(It.IsAny<Company>(), It.IsAny<int>())).Returns(true);
        Assert.IsTrue(mock.Object.InsertCompany(company, 1));
    }

    [TestMethod]
    public void Test_Unit_StaticService()
    {
        var rep = new Mock<ICompanyRepository>();
        rep.Setup(t => t.InsertCompany(It.IsAny<Company>())).Returns(true);

        var serviceMock = new Mock<ICompanyService>();
        serviceMock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);

        Assert.IsTrue(serviceMock.Object.AddTotals(0));
    }

    private Guid GenerateCustomerID()
    {
        return Guid.NewGuid();
    }

因此,当我使用我的2参数InsertCompany方法时,我的IsExecuted方法在验证时失败,如果我将其设置为非虚拟,那么我无法为IsSuccess方法模拟它并且它失败了..

您能否告诉我您的TDD专业知识缺少什么?

1 个答案:

答案 0 :(得分:0)

正如评论中所建议的,你几乎肯定没有测试正确的东西。看看这个测试:

[TestMethod]
public void Test_Unit_InsertCompany_IsSuccess()
{
    Guid id = GenerateCustomerID();
    var company = new Company { CustomerID = id, CompanyName = "CFN-" + id };
    var mock = new Mock<CompanyService>();
    mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);
    mock.Setup(t => t.InsertCompany(It.IsAny<Company>(), It.IsAny<int>())).Returns(true);
    Assert.IsTrue(mock.Object.InsertCompany(company, 1));
}

如果您的类被标记为正确的虚拟状态以便安装程序执行,那么您实际上根本没有测试任何类。你正在设置Mock以在调用方法时返回true,然后断言当你调用方法时mock会返回true ...你只是测试你已经正确设置了Mocks。

当您将方法标记为虚拟时,IsExecuted方法中使用的模拟失败,因为您正在测试的类中调用虚方法。如果你不告诉它,Moq将假设如果你正在做部分模拟,你只需要调用模拟的虚拟,而不是现有的实现。您可以通过设置CallBase标志告诉Moq调用现有实现来覆盖它。

您的测试将变成这样:

[Test]
public void Test_Unit_InsertCompany_IsExecuted() {
    Guid id = GenerateCustomerID();
    var company = new Company { CustomerID = id, CompanyName = "CFN-" + id };
    var mock = new Mock<CompanyService>();
    mock.CallBase = true;
    mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);
    mock.Setup(t => t.InsertCompany(It.IsAny<Company>()));
    mock.Object.InsertCompany(company, 1);
    mock.Verify(t => t.InsertCompany(It.IsAny<Company>()), Times.Once);
}

需要注意的两件事是

  1. 在上面的测试中,CallBase = true用于执行现有代码。
  2. 作为1的结果,有必要为要验证的调用执行安装程序,否则调用将实际执行底层代码并调用存储库。
  3. 测试一个方法在同一个类中调用另一个方法并不是一个好主意。它可能变得非常混乱,并且很难知道您期望发生的事情实际上是发生了什么以及将您的实现与测试代码紧密耦合。

    您已经设置了您的Company类以允许注入存储库。测试相应的存储库交互发生将导致与公司类的实现的耦合较少,并可能导致更直接的模拟。

    使用该方法,您的IsExecuted测试可能会变为:

    [Test]
    public void Test_Unit_InsertCompany_SavesToRepository() {
        Guid id = GenerateCustomerID();
        var repoMock = new Mock<ICompanyRepository>();
        var company = new Company { CustomerID = id, CompanyName = "CFN-" + id };
        var mock = new Mock<CompanyService>(repoMock.Object);
        mock.CallBase = true;
        mock.Setup(t => t.AddTotals(It.IsAny<int>())).Returns(true);
        mock.Object.InsertCompany(company, 1);
        repoMock.Verify(t => t.InsertCompany(It.IsAny<Company>()), Times.Once);
    }
    

    理想情况下,您可以模拟与StaticService.AddTotals的交互,这样您就可以实例化实际的CompanyService,而不是测试中的模拟对象,但这似乎超出了您当前的问题的范围...