单元测试模拟基类

问题描述 投票:0回答:1

需要一些帮助看看我是否有办法测试这个东西。

所以我有一个使用 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。

在基类内部,我正在添加一些新东西,我无法从插件本身外部注入它们。

谢谢

c# unit-testing dependency-injection mef
1个回答
0
投票

我认为你不能模拟基类。您需要破坏该行为才能替换它。我建议一些可以帮助您找到自己的方法的替代方案。前两个需要对基类进行更改。第三个不需要改变。

策略模式

使用策略模式不需要任何额外的库。这可能是一个示例实现。

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 替换您需要的方法。这是一个非常巧妙的解决方案,无需更改原始类即可工作,如果您不拥有它,那就太好了。您可以使用原始类作为构造函数的参数、部分模拟或完全伪造。

免责声明:我即时编写了这些示例,因此它们可能需要一些修复,但我认为您可以明白这个想法。

© www.soinside.com 2019 - 2024. All rights reserved.