c# - 如何部分覆盖类的某些方法以在 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 支持以下两个东西,但都不是很理想:
- 模拟一个界面。未显式模拟的方法返回默认值。这个观点可能看起来很奇怪。
- 模拟一堂课。只有虚方法可以被覆盖。如果我们想以这种方式模拟它们,我们需要更改具体类的代码。
实现这一目标的其他好方法是什么?
还是我解决了错误的问题?也许还有其他方法可以解决此类测试需求?
解决方案
如果该类不是密封的,您可以将要覆盖的方法设为虚拟,然后在派生类中覆盖它们。这将对源代码的影响最小,并且不需要任何库。
请注意,派生类应该只用于测试,并且应该从最终代码中排除,可能使用internal
. 这是一个简单的解决方案,但有点不实用,也不是真正的类模拟,只是一个简单的覆盖来做一些实验和测试。
推荐阅读
- excel - 如何从excel中读取数据并垂直连接列?
- reactjs - 如何使用侦听器重构此 React 组件以使组件正常工作?
- docker - 在 docker-compose 运行的容器中看不到 Dockerfile 集变量
- ios - 扩展 TableViewCells 以匹配其内容高度
- javascript - 如何在每次 Cogntio 用户池更改时触发 Lambda
- javascript - 自上一小时(或最后一天)以来,存储数据变化了 10%
- python - Plotly:如何使用 go.Bar 向堆积条形图添加数据标签?
- python-3.x - Python:有没有办法使用一定数量的输入?
- mysql - Mysql:Is null 不与其他语句一起使用
- windows - Microsoft Windows 中使用了哪些类型的磁盘调度算法?