如果我们不这样做';t验证是否使用单元测试调用了私有方法,我们如何验证它们是否被调用
本文关键字:是否 验证 调用 我们 何验证 有方法 单元测试 这样做 如果 | 更新日期: 2023-09-27 17:59:09
我不想再提起这个问题,但我真的在努力了解如何用我的测试来保护一些东西。
我有一个公共方法(下面),它在调用另一个实际执行某些操作的方法之前先调用一个私有方法。我想确保对私有方法的调用不会被删除,因为这可能是灾难性的。我在这里,这里,这里做了一些研究,他们都说不要测试私人方法。我想我可以理解,但我该如何防止删除这行代码?
正如您所看到的,公共方法返回void,因此我无法测试公共方法调用的结果。我有直接测试ApplicationShouldBeInstalled()
的单元测试。
public void InstallApplications()
{
foreach (App app in this._apps)
{
// This is the line of code that can't be removed. How can I make
// sure it doesn't get removed?
if (!ApplicationShouldBeInstalled(app)) { continue; }
// This simply can't run unless it passes the above call.
CommonUtility.Container.Resolve<IAppInstaller>().InstallApplication(this, app);
}
}
编辑-根据JerKimball的回答,我同意了
基本上,我只使用一个Mock对象(来自Moq),然后验证它的方法是否被调用了预期的次数。
[TestMethod()]
public void ApplicationShouldBeInstalledTest_UseCase13()
{
var mockAppInstaller = new Mock<IAppInstaller>();
mockAppInstaller.Setup(m => m.InstallApplication(It.IsAny<ApplicationServer>(),
It.IsAny<Application>()));
CommonUtility.Container.RegisterInstance<IAppInstaller>(mockAppInstaller.Object);
// Actual test code here
appServer.InstallApplications();
mockAppInstaller.Verify(x => x.InstallApplication(It.IsAny<ApplicationServer>(),
It.IsAny<Application>()), Times.Never());
}
我不能放手;那个剪辑太难看了。尽管我必须创建一个实际的mock类,但这种方法要干净得多:
模拟实现:
public class MockAppInstaller : IAppInstaller
{
public bool Invoked { get; set; }
public void InstallApplication(ApplicationServer server, Application app)
{
this.Invoked = true;
}
}
测试方法:
[TestMethod()]
public void ApplicationShouldBeInstalledTest_UseCase14()
{
MockAppInstaller mockAppInstaller = new MockAppInstaller();
CommonUtility.Container.RegisterInstance<IAppInstaller>(mockAppInstaller);
// Actual test code here
appServer.InstallApplications();
Assert.AreEqual(true, mockAppInstaller.Invoked);
}
我想确保对私有方法的调用不会被删除,因为这可能是灾难性的。
这听起来应该很容易。灾难很容易被发现。因此,运行一个调用公共方法的测试,并检查是否发生了灾难性的事情。防止灾难的私有方法中的代码实际上是运行公共方法的可见副作用。。。而它是由私有方法调用引起的,这是一个实现细节。
因此,在这种情况下,您基本上应该创建一个不应该安装的应用程序(无论出于何种原因),并验证当您调用InstallApplications
时,是否已安装。
这里有一个可能的选项。。。当然,这是非常简单的,但它可能适用于你的情况,只要稍微改变一下;
假设App
看起来像:
public class App
{
public virtual bool CanBeInstalled {get; set;}
}
ApplicationShouldBeInstalled
看起来像:
private bool ApplicationShouldBeInstalled(App app) { return app.CanBeInstalled; }
你可以写一个单元测试,"通过预期的操作进行确认",比如:
void Main()
{
var sut = new ThingToTest();
var mockApp = new Mock<App>();
var wasCanBeInstalledChecked = false;
mockApp
.SetupGet(app => app.CanBeInstalled)
.Callback(() => wasCanBeInstalledChecked = true);
// of course, whatever you'd do here to get an app into this class
sut.AddApp(mockApp.Object);
sut.InstallApplications();
Debug.Assert(wasCanBeInstalledChecked == true);
}
public void InstallApplications()
{
foreach (App app in this._apps.ThatShouldBeInstalled)
{
CommonUtility.Container.Resolve<IAppInstaller>().InstallApplication(this, app);
}
}
我只是想在混合中加入一些横向思考。。。
小心地把它拿出来,不要让它被不小心地拿出来。:)
即使公开此方法也无法查看是否调用了它。通过公开它可以让你测试它,但仅此而已。
如果以常规方式使用if
语句,那么您的代码将更加清晰。它的目的甚至对那些现在正在删除这行的程序员来说都很清楚
public void InstallApplications()
{
foreach (App app in this._apps)
{
if (ApplicationShouldBeInstalled(app)) {
CommonUtility.Container
.Resolve<IAppInstaller>()
.InstallApplication(this, app);
}
}
}
您也可以让这个方法在调用时增加一个计数器。然后,单元测试可以测试此计数器。
注意:您可以将此计数器设为internal
,并使用InternalsVisibleToAttribute
使其对单元测试项目可见。
更新
以下是如何实现此计数器:
public int ApplicationShouldBeInstalled_Count { get; private set; }
public bool ApplicationShouldBeInstalled(App app)
{
ApplicationShouldBeInstalled_Count++;
...
}
然后测试
var sut = new MyInstallerClass();
int oldCount = sut.ApplicationShouldBeInstalled_Count;
sut.InstallApplications();
int newCount = sut.ApplicationShouldBeInstalled_Count;
Assert.AreEqual(oldCount + sut.Apps.Count, newCount);