我有单元测试,调用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);
}
}
答案 0 :(得分: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
}
我认为关键在于您在问题中说“您无法在测试中运行安装代码”。但是,在您需要时验证实际将调用安装代码的循环应该很重要。这应该是已经足够的动力来试图隔离它,无论你是否达到我可能更喜欢的程度。
答案 1 :(得分:1)
当你将它提取到一个接口时,你并没有真正删除属于该类的代码。您需要该类来实现成员。使用接口的一个主要好处是它允许您模拟它们,以便您实际上不会更改功能。您模拟了界面,然后验证特定方法实际上正在执行您希望它执行的操作。
另外,它还允许您使用依赖注入,这样您就不会不断在内存中创建对象实例。