【发布时间】:2020-01-25 22:31:14
【问题描述】:
我有一个代表领域实体的类,而这个类不实现任何接口。让我们考虑一些简单的事情:
public class DomainEntity
{
public DomainEntity(string name) { Name = name; }
public string Name { get; private set; }
}
我正在测试其他一些课程。它有一个接受我的DomainEntity 作为参数的方法,该方法访问Name 属性。例如:
public class EntityNameChecker : IEntityNameChecker
{
public bool IsDomainEntityNameValid(DomainEntity entity)
{
if (entity.Name == "Valid") { return true; }
return false;
}
}
我必须模拟我的DomainEntity 进行测试。我使用NSubstitute 作为我的模拟库(它不允许模拟非虚拟/非抽象属性)。
所以,在不添加接口的情况下(a-la IDomainEntity)我有三个选项来模拟剩余的属性值。
-
将
Name属性设为虚拟:public class DomainEntity { public DomainEntity(string name) { Name = name; } public virtual string Name { get; } }这里的缺点是我不能再将我的
DomainEntity类设为sealed,这意味着任何消费者都可以从它继承并覆盖我的Name属性。 -
创建一个抽象的“基”类,并将基类类型作为参数类型:
public abstract class DomainEntityBase { protected abstract string Name { get; private protected set; } } public sealed class DomainEntity : DomainEntityBase { public DomainEntity(string name) { Name = name; } protected override string Name { get; private protected set; } } public class EntityNameChecker : IEntityNameChecker { public bool IsDomainEntityNameValid(DomainEntityBase entity) { if (entity.Name == "Valid") { return true; } return false; } }这里的缺点是过于复杂。这基本上将抽象类变成了某种接口。
-
不是直接访问
Name属性,而是将Namegetter 转换为方法调用来获取值(我们甚至可以制作方法internal并使用InternalsVisibleTo属性来制作方法对我们的测试程序集可见):[assembly: InternalsVisibleToAttribute("TestAssembly")] public sealed class DomainEntity { private string _name; public DomainEntity(string name) { _name = name; } public string Name => GetName(); internal string GetName() { return _name; } }这里的缺点是……嗯,更多的代码、方法、更多的复杂性(而且为什么要这样编码还不是很明显)。
我的问题是:有没有“首选”的方式来做到这一点?为什么首选?
编辑:
我不想在测试中简单地使用类的实例的原因是构造函数中可能存在其他逻辑。如果我破坏了构造函数,它将破坏所有相关的测试(但它应该只破坏测试 DomainEntity 的测试)。
我可以提取一个接口并完成它。但我更喜欢使用接口来定义行为。这些课程没有。
【问题讨论】:
-
你还没有解释为什么你不只是创建一个接口,或者为什么你首先需要模拟这个类?在您的示例中,您可以只使用
DomainEntity的实例进行测试。 -
据我所知,密封类无法模拟,因此将属性设为虚拟不是问题...您确定可以提取接口吗?
-
@stuartd 该示例已简化。 DomainEntity 的构造函数中可能存在逻辑。如果我破坏了构造函数,它将破坏所有需要 DomainEntity 的测试,这是不可取的。从理论上讲,我可以提取接口,但我认为它不合适。我更喜欢坚持“接口描述行为”,而这些类并没有真正的行为。
-
这正是我们有接口的原因。没有接口就等于没有可测试性。
-
@PhilP.,如果您破坏 DomainEntity 的构造函数,依赖于 DomainEntity 的行为是否仍能正常工作?如果我破坏了某些功能,我希望所有依赖于该功能的测试都会失败。
标签: c# unit-testing abstract-class nsubstitute