C# 在单元测试中处理多个模拟和断言
我目前有一个使用Entity Framework进行CRUD操作的存储库 这是注入到我的服务,需要使用此回购 使用AutoMapper,我将实体模型投影到Poco模型上,Poco由服务返回 如果我的对象有多个属性,那么设置并断言我的属性的正确方法是什么? 如果我的服务有多个repo依赖项,那么设置所有模拟的正确方法是什么*-一个类[设置],其中为这些测试装置配置了所有模拟和对象***** 我希望避免有10个测试,每个测试有50个属性断言和几十个模拟设置。这使得可维护性和可读性变得困难 我读过单元测试的艺术,并没有发现任何关于如何处理这种情况的建议 我使用的工具是Rhino Mocks和NUnit 我也发现了这个,但它没有回答我的问题: 下面是一个示例,它表达了我所描述的内容:C# 在单元测试中处理多个模拟和断言,c#,unit-testing,nunit,repository-pattern,rhino-mocks-3.5,C#,Unit Testing,Nunit,Repository Pattern,Rhino Mocks 3.5,我目前有一个使用Entity Framework进行CRUD操作的存储库 这是注入到我的服务,需要使用此回购 使用AutoMapper,我将实体模型投影到Poco模型上,Poco由服务返回 如果我的对象有多个属性,那么设置并断言我的属性的正确方法是什么? 如果我的服务有多个repo依赖项,那么设置所有模拟的正确方法是什么*-一个类[设置],其中为这些测试装置配置了所有模拟和对象***** 我希望避免有10个测试,每个测试有50个属性断言和几十个模拟设置。这使得可维护性和可读性变得困难 我读过单元
public void Save_ReturnSavedDocument()
{
//Simulate DB object
var repoResult = new EntityModel.Document()
{
DocumentId = 2,
Message = "TestMessage1",
Name = "Name1",
Email = "Email1",
Comment = "Comment1"
};
//Create mocks of Repo Methods - Might have many dependencies
var documentRepository = MockRepository.GenerateStub<IDocumentRepository>();
documentRepository.Stub(m => m.Get()).IgnoreArguments().Return(new List<EntityModel.Document>()
{
repoResult
}.AsQueryable());
documentRepository.Stub(a => a.Save(null, null)).IgnoreArguments().Return(repoResult);
//instantiate service and inject repo
var documentService = new DocumentService(documentRepository);
var savedDocument = documentService.Save(new Models.Document()
{
ID = 0,
DocumentTypeId = 1,
Message = "TestMessage1"
});
//Assert that properties are correctly mapped after save
Assert.AreEqual(repoResult.Message, savedDocument.Message);
Assert.AreEqual(repoResult.DocumentId, savedDocument.DocumentId);
Assert.AreEqual(repoResult.Name, savedDocument.Name);
Assert.AreEqual(repoResult.Email, savedDocument.Email);
Assert.AreEqual(repoResult.Comment, savedDocument.Comment);
//Many More properties here
}
public void Save\u ReturnSavedDocument()
{
//模拟数据库对象
var reposult=new EntityModel.Document()
{
DocumentId=2,
Message=“TestMessage1”,
Name=“Name1”,
Email=“Email1”,
Comment=“Comment1”
};
//创建Repo方法的模拟-可能有许多依赖项
var documentRepository=MockRepository.GenerateStub();
documentRepository.Stub(m=>m.Get()).IgnoreArguments().Return(new List())
{
报告结果
}.AsQueryable());
documentRepository.Stub(a=>a.Save(null,null)).IgnoreArguments().Return(reposult);
//实例化服务并注入repo
var documentService=新的documentService(documentRepository);
var savedDocument=documentService.Save(新模型.Document()
{
ID=0,
DocumentTypeId=1,
Message=“TestMessage1”
});
//断言属性在保存后已正确映射
Assert.AreEqual(reposult.Message、savedDocument.Message);
Assert.AreEqual(reposult.DocumentId、savedDocument.DocumentId);
Assert.AreEqual(reposult.Name、savedDocument.Name);
Assert.AreEqual(reposult.Email、savedDocument.Email);
Assert.AreEqual(reposult.Comment、savedDocument.Comment);
//这里还有很多房产
}
考虑使用匿名类型:
public void Save_ReturnSavedDocument()
{
// (unmodified code)...
//Assert that properties are correctly mapped after save
Assert.AreEqual(
new
{
repoResult.Message,
repoResult.DocumentId,
repoResult.Name,
repoResult.Email,
repoResult.Comment,
},
new
{
savedDocument.Message,
savedDocument.DocumentId,
savedDocument.Name,
savedDocument.Email,
savedDocument.Comment,
});
}
有一件事需要注意:可为null的类型(例如int?)和可能具有稍微不同类型的属性(float vs double)-但您可以通过将属性强制转换为特定类型(例如(int?)reposult.DocumentId)来解决这一问题
另一个选项是创建一个自定义的断言类/方法。基本上,技巧是将尽可能多的混乱推到unittests之外,这样只有 这有待检验 一些方法可以做到这一点:
然后,返回SUT的helper方法不会将模拟存储库作为参数,而是使用属性来构造SUT。您可能需要重新初始化每个
[TestInitialize]
上的每个存储库属性void AssertPocoEqualsModel(Poco p,Model m)
)。同样,这消除了一些混乱,您可以免费获得可重用性当不测试属性映射时,您只需验证SUT是否返回正确的对象实例(即基于
Id
或Name
),以及是否仅返回可能更改的属性(通过当前测试的业务逻辑)
包含正确的值(例如订单总数)[TestClass]
公共类DocumentServiceTest
{
私有IDocumentrepositorymock{get;set;}
[测试初始化]
公共无效初始化()
{
DocumentRepositoryMock=MockRepository.GenerateSub();
}
[测试方法]
public void Save_ReturnSavedDocument()
{
//安排
var reposult=TestData.AcmeDocumentEntity;
DocumentRepositoryMock
.Stub(m=>m.Get())
.IgnoreArguments()
[TestClass]
public class DocumentServiceTest
{
private IDocumentRepository DocumentRepositoryMock { get; set; }
[TestInitialize]
public void Initialize()
{
DocumentRepositoryMock = MockRepository.GenerateStub<IDocumentRepository>();
}
[TestMethod]
public void Save_ReturnSavedDocument()
{
//Arrange
var repoResult = TestData.AcmeDocumentEntity;
DocumentRepositoryMock
.Stub(m => m.Get())
.IgnoreArguments()
.Return(new List<EntityModel.Document>() { repoResult }.AsQueryable());
DocumentRepositoryMock
.Stub(a => a.Save(null, null))
.IgnoreArguments()
.Return(repoResult);
//Act
var documentService = CreateDocumentService();
var savedDocument = documentService.Save(TestData.AcmeDocumentModel);
//Assert that properties are correctly mapped after save
AssertEntityEqualsModel(repoResult, savedDocument);
}
//Helpers
private DocumentService CreateDocumentService()
{
return new DocumentService(DocumentRepositoryMock);
}
private void AssertEntityEqualsModel(EntityModel.Document entityDoc, Models.Document modelDoc)
{
Assert.AreEqual(entityDoc.Message, modelDoc.Message);
Assert.AreEqual(entityDoc.DocumentId, modelDoc.DocumentId);
//...
}
}
public static class TestData
{
public static EntityModel.Document AcmeDocumentEntity
{
get
{
//Note that a new instance is returned on each invocation:
return new EntityModel.Document()
{
DocumentId = 2,
Message = "TestMessage1",
//...
}
};
}
public static Models.Document AcmeDocumentModel
{
get { /* etc. */ }
}
}
var repoResult = _fixture.Create<EntityModel.Document>();
Assert.AreEqual(repoResult.Message, savedDocument.Message);
repoResult.Message.Should().Be(savedDocument.Message);