2

我有单元测试调用ApplicationShouldBeInstalled(app)以确保它正常工作。下面的实际生产代码也调用了该方法,因此它不会意外安装应用程序。但是,没有什么能阻止开发人员删除完成检查的那行代码。而且我的单元测试不会捕捉到这一点,因为测试是在测试ApplicationShouldBeInstalled(app)方法,而不是InstallApplications()方法。

我无法InstallApplications()从我的测试代码中调用,因为它会尝试安装应用程序。InstallApplication(app)是同一个类中的一个方法,而不是另一个我可以用接口模拟它的类。有没有办法确保 InstallApplications() 始终执行该检查?我想我可以转移ApplicationShouldBeInstalled(app)到另一个班级并模拟它,但是我只是为了测试/模拟而移动代码。有没有更好的办法?

public void InstallApplications()
{
    foreach (App app in this._apps)
    {
        if (!ApplicationShouldBeInstalled(app)) { continue; }

        InstallApplication(app);
    }                       
}

模拟选项看起来像这样。将Container在实时运行时返回真实的实现,并在运行测试时返回模拟。

public void InstallApplications()
{
    foreach (App app in this._apps)
    {
        if (!ApplicationShouldBeInstalled(app)) { continue; }

        Container.Resolve<IInstaller>().InstallApplication(app);
    }                       
}
4

2 回答 2

2

是的,从处理安装的代码中删除控制是否应安装应用程序的策略的代码。这将允许您单独测试两段代码,并确信每段代码都在执行您的要求。我什至会在这里有 3 个合作者。控制循环的代码,控制验证策略的代码,最后是执行安装的代码。三件套,可独立测试,更容易验证。

foreach (var app in this._apps)
{
     if (!applicationInstallationPolicyProvider.CanInstall(app)) // can be mocked away
     {
          continue;
     }

     applicationInstaller.Install(app); // can also be mocked away
}

我认为你的关键是当你在问题中说“你不能在测试中运行安装代码”时。但对您来说,验证循环是否在需要时调用安装代码应该很重要。无论您是否达到我可能喜欢的程度,这应该已经足以成为尝试隔离它的动力。

于 2013-02-14T19:57:52.020 回答
1

当您将其提取到接口时,您并没有真正删除属于该类的代码。您需要该类来实现成员。使用接口的一个主要好处是它允许您模拟它们,这样您就不会实际更改功能。你模拟接口然后你验证一个特定的方法实际上正在做你期望它做的事情。

On a side note it also allows you to use dependency injection so that your not creating instances of objects in memory constantly.

于 2013-02-14T19:58:44.310 回答