我想对一个函数进行单元测试,该函数调用其实现中的基类(使用.base())
我不能使用Mocking,因为这是我们处理的继承,所以我没有在构造函数中获取对象。
示例代码:
protected override BuyerDeal Map(BuyerDealDTO buyerDealDTO, BuyerDeal buyerDealEntity)
{
buyerDealEntity.prop1 = buyerDealDTO.prop2;
base.Map(buyerDealDTO, buyerDealEntity);
return buyerDealEntity;
}
我想测试这个功能,但我不想这样:
base.Map(buyerDealDTO, buyerDealEntity);
发生,因为我自己测试基地。
但我确实想测试(验证)呼叫,并且仅测试对基站的呼叫。
顺便说一句,基类是抽象。
问题是如果从该基类继承的类很少,这将导致多次测试基类。
答案 0 :(得分:2)
在不了解您的映射器的情况下,可能有几种方法可以做到这一点:
将代码和地图代码拆分为单独的方法,例如:
protected override BuyerDeal Map(BuyerDealDTO buyerDealDTO, BuyerDeal buyerDealEntity)
{
ExtraLogic(...);
base.Map(buyerDealDTO, buyerDealEntity);
return buyerDealEntity;
}
protected void ExtraLogic(...)
{
buyerDealEntity.prop1 = buyerDealDTO.prop2;
}
然后您可以测试ExtraLogic。这可能不适用于所有代码,因为可能需要调用base作为依赖项,这会更改流程,如注释所述。 我一般不会推荐这个。
或者不从基类继承。继承Interface
然后嘲笑你的抽象类。支持组合而不是继承(FCoI)。
这允许您注入基类并仅测试代码。
如果没有定义接口(无论如何都是坏的),或者框架明确地使用抽象类,可能不起作用。
答案 1 :(得分:1)
我看到三种方法:
尝试忽略派生类中的基类'试验。这里的问题是你的测试是不完整的,因为你从不测试派生类的相互作用。代码与基类。
测试装置的并行继承层次结构,即基类的抽象装置,由派生类的所有装置继承。这有助于消除重复并测试上述交互,但使得固定装置难以理解。
尽可能遵循Composition over Inheritance 。没有基类,没有头痛。
我建议遵循第三个,特别是因为看起来买方交易之间的映射可能被认为是一个单独的责任。
底线:只需创建一个BuyerDealMapper
,对其进行测试,并在测试其他类时进行模拟。
答案 2 :(得分:0)
进行单元测试时,您实际上正在测试测试中的代码为相应的输入提供正确的输出。
此输入可能以依赖关系的形式出现,例如方法参数,从文件系统或从网络源读取的数据。
输出可以是从方法返回的值,写入文件系统的数据或传递到另一个类的属性的形式。
只要您的输入产生预期的输出,在合理范围内,在测试代码中发生的任何其他内容都无关紧要。
在您发布的代码中,您的单元测试不应该关注是否对base.Map(buyerDealDTO, buyerDealEntity);
进行了调用 - 只是测试中的代码给出了预期的输出。
如果您的基类本身可能需要依赖项,例如文件系统写入或网络读取等等,则可以在测试启动例程中模拟这些依赖项。
如果您担心自己多次测试基类,这实际上是一件非常好的事情!测试代码的次数越多,代码看到的条件和情况就越多,越彻底,那段代码就越有弹性。您的单元测试应该有助于保证您的基类代码和处理您继承的类使用它。
例如,如果基类抛出异常,您的方法将如何处理?返回意外值?因某种原因不归?单元测试应考虑这些因素。
当然,如果基类调用与方法及其工作方式无关,那么也许根本不应该在该方法中调用它。可能需要一些代码重构来解决这个问题。事实上,这是单元测试的巨大好处,因为它也可以帮助您解决诸如此类的架构问题。当然,假设您正在使用TDD
- 而不是测试预先编写的代码 - 这可能是一个很大的帮助。
<强>重构强>
重构此代码的一种可能方法是不在overriden方法中调用基类方法。让基类确保自己调用此方法,然后在重写的代码中调用所需的代码。例如:
public abstract class MyBaseClass
{
public BuyerDeal Map(BuyerDealDTO buyerDealDTO, BuyerDeal buyerDealEntity)
{
// perform base class logic here
var entity = this.MapInternal(buyerDealDTO, buyerDealEntity);
return entity;
}
protected abstract BuyerDeal MapInternal(BuyerDealDTO buyerDealDTO, BuyerDeal buyerDealEntity);
}
public class MyClass : MyBaseClass
{
protected override BuyerDeal MapInternal(BuyerDealDTO buyerDealDTO, BuyerDeal buyerDealEntity)
{
buyerDealEntity.prop1 = buyerDealDTO.prop2;
return buyerDealEntity;
}
}
使用此方法,当您对MyClass
进行单元测试时,您不再多次测试MyBaseClass::Map(...)
。当然,您仍然需要单独测试MyBaseClass
本身。