C# 对“复杂”应用程序服务进行单元测试的正确方法
我有一个应用服务,即大型方法,负责协调多个业务对象之间的交互。本质上,它从一个包含客户信息和发票的系统中获取DTO,然后根据各种业务规则将其转换并导入到不同的系统中C# 对“复杂”应用程序服务进行单元测试的正确方法,c#,unit-testing,testing,methods,C#,Unit Testing,Testing,Methods,我有一个应用服务,即大型方法,负责协调多个业务对象之间的交互。本质上,它从一个包含客户信息和发票的系统中获取DTO,然后根据各种业务规则将其转换并导入到不同的系统中 public void ProcessQueuedData() { var queuedItems = _importServiceDAL.LoadQueuedData(); foreach (var queuedItem in queuedItems) {
public void ProcessQueuedData()
{
var queuedItems = _importServiceDAL.LoadQueuedData();
foreach (var queuedItem in queuedItems)
{
var itemType = GetQueuedImportItemType(queuedItem);
if (itemType == QueuedImportItemType.Invoice)
{
var account = _accountDAL.GetAccountByAssocNo(queuedItem.Assoc);
int agentAccountID;
if (!account.IsValid)
{
agentAccountId = _accountDAL.AddAccount(queuedItem.Assoc);
}
else
{
agentAccountId = account.AgentAccountID;
}
/// Do additional processing TBD
}
}
}
对于单元测试,假设服务中的整个过程都应该在粒度逐步的基础上进行测试,这是否正确,类似于以下内容
导入服务\u进程队列数据\u调用数据访问层\u加载队列
ImportService_ProcessQueuedData_与QueuedItemToProccess_ChecksIfAccountExists
使用发票调用导入服务\u处理队列数据\u LTOCEATEACCOUNTIFONE无文本列表
下面是一个典型的测试:
[TestMethod()]
public void ImportService_ProcessQueuedData_WithInvoice_CallsDALToCheckIfAgentAccountExists()
{
var accountDAL = MockRepository.GenerateStub<IAccountDAL>();
var importServiceDAL = MockRepository.GenerateStub<IImportServiceDAL>();
importServiceDAL.Stub(x => x.LoadQueuedData())
.Return(GetQueuedImportItemsWithInvoice());
accountDAL.Stub(x => x.GetAccountByAssocNo("FFFFF"))
.IgnoreArguments()
.Return(new Account() { AgentAccountId = 0 });
var importSvc = new ImportService(accountDAL, importServiceDAL);
importSvc.ProcessQueuedData();
accountDAL.AssertWasCalled(a => a.GetAccountByAssocNo("FFFFF"), o => o.IgnoreArguments());
accountDAL.VerifyAllExpectations();
}
我的问题是,我在每一个测试中都做了太多的设置,这变得很脆弱。这是正确的方法吗?如果是的话,有哪些指针可以避免在每个粒度测试中重复所有的设置 我对您的特定应用不太了解,因此我无法提出任何具体建议。这就是说,这种面向过程的测试听起来可能是采用技术的一个很好的候选者。我熟悉工具公平披露:不久前,我被聘为Java这个工具的开发人员,但是似乎有一个等价的工具叫做C,还有一本附带的书。我这样说的原因是,也许您可以随机地遍历整个过程,将您的设置代码全部放在一个地方,并允许抽象测试生成为您完成大部分剩余的艰苦工作。我个人会尝试测试所有代码片段,但不必将每个部分作为自己的测试。一个测试检查具有有效帐户的发票是否通过。第二个测试检查具有无效帐户的发票是否创建了新帐户。当然,我会模拟DAL,这样就不会向数据库中添加任何数据。这也允许模拟异常和不应执行任何操作的情况—队列中没有任何内容,或者可能没有发票。我同意佩德罗的观点。您的第一个示例测试ImportService\u ProcessQueuedData\u CallsDataAccessLayer\u ToLoadQueue是不必要的,因为其他测试将隐式测试调用的LoadQueuedData-如果不是,它们将没有可操作的数据。您希望对每个路径进行测试,但不需要对方法中的每一行代码进行单独的测试。如果有六条通过条件分支的路径,则需要六个测试
<>如果我想得到真正的幻想,我也可以考虑利用对象多态性来减少IF语句的数量并简化测试。例如,您可以为每个QueuedImportItemType使用不同的处理程序,并将处理该类型项的逻辑放在那里,而不是放在大流程方法中。将迭代逻辑和对每种类型项目的处理分开,使它们更易于单独测试。谢谢Gian。。。您是否建议NModel b/c感知到的可能输入条件数量巨大?如果输入类型的组合更加有限,您还会推荐这种方法吗?我尝试测试的整个过程实际上只有6个条件分支和已知数量的条件,这些条件在if-then逻辑中起作用。再次感谢您的见解。更重要的是,这似乎是一个连续的过程,每一步都有可能捕捉到错误条件,并且可能通过这些操作有多个不同的路径。这就是基于模型的测试真正闪耀的地方——您只需对模型中的所有状态进行编码,让测试系统找出可能的路径。