C# 重构服务层类
我的公司正在进行单元测试,而我在重构服务层代码方面遇到了一些麻烦。下面是我编写的一些代码的示例:C# 重构服务层类,c#,unit-testing,refactoring,C#,Unit Testing,Refactoring,我的公司正在进行单元测试,而我在重构服务层代码方面遇到了一些麻烦。下面是我编写的一些代码的示例: public class InvoiceCalculator:IInvoiceCalculator { public CalculateInvoice(Invoice invoice) { foreach (InvoiceLine il in invoice.Lines) { UpdateLine(il); } //do
public class InvoiceCalculator:IInvoiceCalculator
{
public CalculateInvoice(Invoice invoice)
{
foreach (InvoiceLine il in invoice.Lines)
{
UpdateLine(il);
}
//do a ton of other stuff here
}
private UpdateLine(InvoiceLine line)
{
line.Amount = line.Qty * line.Rate;
//do a bunch of other stuff, including calls to other private methods
}
}
在这种简化的情况下(它从一个1000行的类减少到只有1个公共方法和~30个私有方法),我的老板说我应该能够分别测试CalculateInvoice和UpdateLine(UpdateLine实际上调用了3个其他私有方法,并执行数据库调用)。但是我该怎么做呢?他建议的重构对我来说似乎有点复杂:
//Tiny part of original code
public class InvoiceCalculator:IInvoiceCalculator
{
public ILineUpdater _lineUpdater;
public InvoiceCalculator (ILineUpdater lineUpdater)
{
_lineUpdater = lineUpdater;
}
public CalculateInvoice(Invoice invoice)
{
foreach (InvoiceLine il in invoice.Lines)
{
_lineUpdater.UpdateLine(il);
}
//do a ton of other stuff here
}
}
public class LineUpdater:ILineUpdater
{
public UpdateLine(InvoiceLine line)
{
line.Amount = line.Qty * line.Rate;
//do a bunch of other stuff
}
}
我可以看到依赖关系现在是如何被打破的,我可以测试这两个部分,但这也会从我原来的类中创建20-30个额外的类。我们只在一个地方计算发票,所以这些零件不会真正重复使用。这是做出改变的正确方式,还是你会建议我做些不同的事情
谢谢大家!
杰斯是的,不错。我不确定InvoiceLine对象是否也包含一些逻辑,否则您可能还需要IInvoiceLine。 我有时也有同样的问题。一方面,您希望做正确的事情并对代码进行单元测试,但当涉及到数据库调用和文件写入时,它会导致大量额外的工作,以使用所有TestObject设置第一次测试,这些TestObject将在文件写入和数据库io即将发生时介入,接口,断言,并且您还希望测试数据层是否不包含任何错误。因此,一个比“单元”更“过程”的测试通常更容易构建。 如果您有一个项目(将来)会发生很多变化,并且该代码有很多依赖项(可能其他程序读取文件或数据库数据),那么最好对代码的所有部分进行可靠的单元测试,而且投资时间是值得的。 但是如果这个项目像我的最新客户所说的那样“让我们把它上线,也许明年我们会做一些调整,明年会有一些新的东西”,那么我就不会对自己太难让所有的单元测试都启动并运行了
Michel在我看来,这一切都取决于UpdateLine()方法的“重要性”。如果它只是一个实现细节(例如,它可以很容易地内联到CalculateInvoice()方法中,并且它们唯一会影响可读性),那么您可能不需要将它与主类分开进行单元测试 另一方面,如果UpdateLine()方法对业务逻辑有一些价值,如果您可以想象需要独立于类的其余部分更改此方法(并因此单独测试)的情况,那么您应该继续将其重构为单独的LineUpdater类
您可能不会以这种方式得到20-30个类,因为大多数私有方法实际上只是实现细节,不值得单独测试。好吧,您的老板在单元测试方面做得更正确:
他现在可以在不测试UpdateLine()函数的情况下测试CalculateInvoice()。他可以传递模拟对象而不是真正的LineUpdater对象,并且只测试CalculateInvoice(),而不是一大堆代码。
是这样吗?视情况而定。你的老板想做真正的单元测试。第一个例子中的测试不是单元测试,而是集成测试 集成测试之前的单元测试有哪些优点?
1) 单元测试只允许测试一个方法或属性,而不受其他方法/数据库等的影响。
2) 第二个优点-单元测试执行得更快(例如,您说过UpdateLine使用数据库),因为它们不会测试所有嵌套方法。嵌套方法可以是数据库调用,因此如果您有数千个测试,那么您的测试可能会运行得很慢(几分钟)。
3) 第三个优点:如果您的方法进行数据库调用,那么有时您需要设置数据库(用测试所需的数据填充数据库),这并不容易——也许您需要编写几页代码来为测试准备数据库。通过单元测试,可以将数据库调用与正在测试的方法分离(使用模拟对象) 但是!我不是说单元测试更好。他们只是不同而已。正如我所说,单元测试允许您在隔离和快速的情况下测试单元。集成测试更容易,允许您测试不同方法和层的联合工作的结果。老实说,我更喜欢集成测试:) 另外,我还有一些建议给你:
1) 我不认为有数量字段是个好主意。似乎Amount字段是额外的,因为它的值可以基于其他两个公共字段进行计算。如果您仍要执行此操作,我会将其作为一个只读属性执行,该属性返回Qty*Rate。
2) 通常,拥有一个包含1000行的类可能意味着它的设计很糟糕,应该进行重构
现在,我希望你能更好地了解情况并做出决定。另外,如果你了解情况,你可以和你的老板谈谈,然后一起做出决定。你老板的例子在我看来是合理的 在为任何场景进行设计时,我尝试记住的一些关键注意事项包括:
line.Amount = line.Qty * line.Rate;
var amount = line.CalculateAmount();