【问题标题】:Mock method of system-under-test with Moq使用 Moq 模拟被测系统的方法
【发布时间】:2014-07-30 17:01:30
【问题描述】:

我在 CompanyApplication 类中有以下三个方法(以及列出的支持工厂和服务):

public ResultSet<CompanyDto> AddCompany(CompanyDto companyDto)
{
    var result = new CompanyDto();
    var company = new Company();
    Mapper.Map(companyDto, company);

    using (ITransaction t = _transactionFactory.Create())
    {
        company = _companyService.Add(company);
        t.Commit();
    }

    Mapper.Map(company, result);
    return new ResultSet<CompanyDto>(1, new[] { result });
}

public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
{
    var result = new CompanyContactDto();
    var company = new Company();
    var contact = new CompanyContact();
    Mapper.Map(companyContactDto, contact);

    using (ITransaction t = _transactionFactory.Create())
    {
        var contactCompanies = FindByIdJoin<Company, CompanyDto>(companyContactDto.CompanySK);
        Mapper.Map(contactCompanies.Data.First(), company);
        company.CompanyContacts.Add(contact);
        company = _companyService.Update(company);
        t.Commit();
    }

    Mapper.Map(contact, result);
    return new ResultSet<CompanyContactDto>(1, new[] { result });
}

public ResultSet<T_DtoType> FindByIdJoin<T_DbType, T_DtoType>(long id)
{
    IAbstractRepository<T_DbType> repository = EnsureRepository<T_DbType>();
    T_DbType entity = repository.FindByIdJoin(id);
    return (entity == null ? null : MapResultSetToDto<T_DbType, T_DtoType>(entity));
}

这里还有其他对象在起作用,这就是为什么 FindByIdJoin 在 CompanyApplication 类中成为一个单独的方法。

我已经用一些模拟和 CompanyApplication 类的实例设置了测试类:

private Mock<ICompanyRepository> _mockCompanyRepository;
private Mock<ICompanyDomain> _mockCompanyService;
private Mock<ITransactionFactory> _mockTransactionFactory;
private Mock<ITransaction> _mockTransaction;
private CompanyApplication _companyApplication;

[Setup]
public void SetUp()
{
    _mockCompanyRepository = new Mock<ICompanyRepository>(MockBehavior.Strict);
    _mockCompanyService = new Mock<ICompanyDomain>(MockBehavior.Strict);
    _mockTransactionFactory = new Mock<ITransactionFactory>(MockBehavior.Strict);
    _mockTransaction = new Mock<ITransaction>(MockBehavior.Strict);

    _companyApplication = new CompanyApplication(
        _mockCompanyRepository.Object,
        _mockCompanyService.Object,
        _mockTransactionFactory.Object);
}

我可以像这样直接在 Moq 中成功地测试 FindByIdJoin 和 AddCompany 方法:

[Test]
public void CanFindCompanyByIdJoin()
{
    var data = new Company {ObjectId = 1, Name = "Company1"};
    _mockCompanyRepository.Setup(x => x.FindByIdJoin(It.Is<long>(arg => arg == data.ObjectId)))
        .Returns(data);

    var result = _companyApplication.FindByIdJoin<Company, CompanyDto>(data.ObjectId);

    Assert.AreEqual(data.ObjectId, result.Data.First().ObjectId);
}

[Test]
public void CanAddCompany()
{
    var data = new Company {ObjectId = 1, Name = "Company1"};
    _mockCompanyService.Setup(x => x.Add(It.Is<Company>(arg => arg.ObjectId == data.ObjectId)))
        .Returns(data);

    _mockTransactionFactory.Setup(x => x.Create()).Returns(_mockTransaction.Object);
    _mockTransaction.Setup(x => x.Commit());
    _mockTransaction.Setup(x => x.Dispose());

    var dto = new CompanyDto {ObjectId = 1, Name = "Company1"};
    var result = _companyApplication.AddCompany(dto);

    _mockCompanyService.Verify(t => t.Add(It.IsAny<Company>()));
}

这两个测试通过就好了。但是,我在为AddCompanyContact 进行测试时遇到了麻烦,因为它调用FindByIdJoin 作为其流程的一部分,而这似乎妨碍了。

具体来说,有没有办法在AddCompanyContact 方法的测试中模拟var contactCompanies = FindByIdJoin&lt;Company, CompanyDto&gt;(companyContactDto.CompanySK)?

谢谢!

【问题讨论】:

    标签: c# unit-testing mocking moq


    【解决方案1】:

    根据您想要做的工作量,我看到了两种选择。

    1. 将该调用包装到一个对象中并使用 IOC 容器对其进行实例化。如果您还没有使用它,我觉得这是最费力的一个。
    2. 将该调用转换为 Func 并创建一个没有执行调用的参数的方法。这种方法的缺点是顶部调用不可测试,但允许访问该方法的其余部分。

    以下示例:

    public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto)
    {
             AddCompanyContact(CompanyContactDto, ()=>
                                                  {
                                                     return FindByIdJoin<Company, CompanyDto> companyContactDto.CompanySK); 
                                                  }
    }
    
    public ResultSet<CompanyContactDto> AddCompanyContact(CompanyContactDto companyContactDto, Func<WhateverTheMethodReturns> findIdReplacement)
    {
        var result = new CompanyContactDto();
        var company = new Company();
        var contact = new CompanyContact();
        Mapper.Map(companyContactDto, contact);
    
        using (ITransaction t = _transactionFactory.Create())
        {
            var contactCompanies = findIdReplacement();
            Mapper.Map(contactCompanies.Data.First(), company);
            company.CompanyContacts.Add(contact);
            company = _companyService.Update(company);
            t.Commit();
        }
    
        Mapper.Map(contact, result);
        return new ResultSet<CompanyContactDto>(1, new[] { result });
    }
    

    【讨论】:

      【解决方案2】:

      我把问题复杂化了……因为AddCompanyContact 调用FindByIdJoin,我需要做的就是模拟FindByIdJoin 中使用的相同接口。

      经验教训:模拟接口,而不是类。

      【讨论】:

        猜你喜欢
        • 2021-10-10
        • 1970-01-01
        • 2014-02-23
        • 2010-11-12
        • 2014-01-18
        • 2011-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多