需要一些帮助看看我是否有办法测试这个东西。
所以我有一个使用 MEF 插件的应用程序,该插件接受带有多个道具的接口,其中一个道具是 ActionType。
然后在这个特定的插件中,主要目标是执行一些操作,操作本身在不同的地方写入内容,位置和值取决于操作。
所以我有一个接口,我们称之为 IBaseAction,它只有一个方法 SendAction,它接受一些参数:
public interface IBaseAction{
bool SendAction(string actionName, string actionPlace, string actionValue)
}
然后我有这个基类,它实现了接口,并且具有编写、检查和记录所有内容的逻辑。
public class BaseAction:IBaseAction
{
public virtual bool SendAction(string actionName, string actionPlace, string actionValue)
{
//Something something...
}
}
因此,对于每个可用的操作,我都有一个新的 Action 类,该类派生自 Base 类并重写基本方法,对于其中的每一个,它都会在调用基本 SendAction 方法之前执行一些逻辑。
public class ActionA:BaseClass
{
public override bool SendAction(string actionName, string actionPlace, string actionValue)
{
//Some logic... Transformations... Custom Data Preparations...
base.SendAction(actionName,actionPlace,actionValue)
}
}
应用程序/插件的入口点,我将有一个 switch 语句来实例化正确的操作,如下所示:
IBaseAction baseAction;
switch (ActionType)
{
case "A":
baseAction = new ActionA();
break;
case "B":
baseAction = new ActionB();
break;
//Other cases...
}
然后我只需调用baseAction.SendAction(...);
现在我正在尝试为此创建单元测试,所以我需要知道我是否可以以某种方式模拟、替换基类?
我只需要测试不同的 Actions 逻辑,然后模拟基类的工作,随意返回 true 或 false。
在基类内部,我正在添加一些新东西,我无法从插件本身外部注入它们。
谢谢
我认为你不能模拟基类。您需要破坏该行为才能替换它。我建议一些可以帮助您找到自己的方法的替代方案。前两个需要对基类进行更改。第三个不需要改变。
使用策略模式不需要任何额外的库。这可能是一个示例实现。
public interface ISendStrategy {
bool SendAction(string actionName, string actionPlace, string actionValue);
}
public class BaseAction: IBaseAction {
// make this visible to your test project with InternalsVisibleTo
internal ISendStrategy SendActionStrategy {get; set;}
public BaseAction()
{
this.SendActionStrategy = new SomeDefaultImplementation();
}
public virtual bool SendAction(string actionName, string actionPlace, string actionValue)
{
this.SendActionStrategy.SendAction (....)
}
}
为了进行测试,您将提供适合您需求的虚假策略。您可以根据自己的喜好进行调整:使用 setter、在构造函数中注入策略等...
现在,如果您正在使用像 NSubstitute 这样的模拟库,您可以执行类似的操作。
public class BaseAction: IBaseAction {
public virtual bool SendAction(string actionName, string actionPlace, string actionValue)
{
this.SendActionImplementation (....)
}
public virtual bool SendActionImplementation (string actionName, string actionPlace, string actionValue)
{
// the real thing.
}
}
然后,在您的测试中,如果您想使用部分模拟,则给定一个后代类 BaseActionA。
var substitute = Substitute.ForPartsOf<ActionA>()
subsitute.Configure().SendActionImplementation (any,any,any).ReturnsForAnyArgs(false);
并不是说我喜欢
SendActionImplementation
公开或虚拟。根据文档,使其internal virtual
可能会导致问题。我会选择第一种方法。
让ActionA实现桥接模式,但不扩展基类
public ActionA: IBaseAction {
private IBaseAction real;
public ActionA (IBaseAction base) {
this.real = base;
}
public bool SendAction(...)
{
// custom
return this.real.SendAction()
}
// and the remaining methods as well
}
如果接口有很多方法,您可以创建一个包含所有方法的中间类,并让 ActionA 替换您需要的方法。这是一个非常巧妙的解决方案,无需更改原始类即可工作,如果您不拥有它,那就太好了。您可以使用原始类作为构造函数的参数、部分模拟或完全伪造。
免责声明:我即时编写了这些示例,因此它们可能需要一些修复,但我认为您可以明白这个想法。