首页 > 解决方案 > 如何部分覆盖类的某些方法以在 C# 中进行测试,同时保持原始类定义完整?

问题描述

问题

我们希望找到一种方法来选择性地覆盖类的某些方法,但不更改其定义。
此外,我们希望覆盖是可选的(即不是强制性的)。也就是说,我们可以随时覆盖它,但我们也可以选择在我们不想的时候不为它编写任何代码行。

用例

假设我们有一个提供数据的类,我们称之为Model
我们有另一个使用这些数据的类。它是View

现在,我们想为View. 为方便起见,我们希望更改提供的部分数据,Model同时保持其他部分不变。
因为我们想改变返回的数据Model,这意味着我们必须重写它的一些行为(即方法)。不过,因为这只是一个测试,我们自然不想修改Model.

以前的尝试

装饰器

我能想到的第一种方法是提取一个接口Model,比如说IModel,并为它创建一个装饰器,例如

public interface IModel
{
    string GetSomething();
    string GetOther(); // We want to override this
}

public class Model : IModel // This is the real model
{
}

public class ModelWrapper : IModel
{
    private IModel _internal; // Should be an instance of Model

    public string GetSomething() => _internal.GetSomething(); // we want to keep the behavior of original model for this method

    public string GetOther() // We want to change the behavior of this one
    {
        return "something for testing";
    }
}

这种方法的优点是我们将完全控制模拟。但是,不利的一面是我们必须继续维护ModelWrapper.
IModel改变时,我们被迫更新ModelWrapper
不幸的是,我们有很多IModel,而且它们确实经常变化。维护这些包装类是相当令人不安的。

模拟库

我能想到的另一种方法是使用模拟库来执行此操作,例如 NSubtitute 或 Moq。
使用模拟库,我们可以指定接口方法的行为,而无需更改其实现类的任何代码。

我们不必指定接口的每个方法的行为。我们可以有选择地模拟它们。
未模拟方法的行为由模拟库自动决定。通常他们只是简单地返回返回类型的默认值。

但是,有时默认值对我们来说可能不够好,因为我们可能需要它们在某个特定范围内,或者IView看起来很奇怪。更好的后备可能是使用我们的(现有)具体类。他们通常会产生正确的结果。

到目前为止,我找不到可以回退到具体类的模拟库。NSubstitute & Moq 支持以下两个东西,但都不是很理想:

  1. 模拟一个界面。未显式模拟的方法返回默认值。这个观点可能看起来很奇怪。
  2. 模拟一堂课。只有虚方法可以被覆盖。如果我们想以这种方式模拟它们,我们需要更改具体类的代码。

实现这一目标的其他好方法是什么?
还是我解决了错误的问题?也许还有其他方法可以解决此类测试需求?

标签: c#testingmocking

解决方案


如果该类不是密封的,您可以将要覆盖的方法设为虚拟,然后在派生类中覆盖它们。这将对源代码的影响最小,并且不需要任何库。

请注意,派生类应该只用于测试,并且应该从最终代码中排除,可能使用internal. 这是一个简单的解决方案,但有点不实用,也不是真正的类模拟,只是一个简单的覆盖来做一些实验和测试。


推荐阅读