用Moq模拟测试系统的方法

时间:2014-07-30 17:01:30

标签: c# unit-testing mocking moq

我在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中测试FindByIdJoinAddCompany方法,如下所示:

[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作为其流程的一部分,而这似乎正在阻碍。

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

谢谢!

2 个答案:

答案 0 :(得分:0)

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

  1. 将该调用包装到对象中并使用IOC容器对其进行实例化。如果您还没有使用过,我认为这是最省心的。
  2. 将该调用转换为Func并创建一个没有该参数进行调用的方法。这种方法的缺点是top调用是不可测试的,但允许访问方法的其余部分。
  3. 以下示例:

    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 });
    }
    

答案 1 :(得分:0)

我过度使问题变得复杂......因为AddCompanyContact调用了FindByIdJoin,所以我需要做的就是模仿FindByIdJoin中使用的相同界面。

获得的经验:模拟接口,而不是类。