你可能已经了解了这个问题,但是我认为你试图测试的东西似乎没有太多意义。仅测试 ThrowIfCancellationRequested
被调用的次数与 Upload
并没有确保它们按正确的顺序调用,而我假设在这种情况下实际上是相关的。您不希望像这样的代码通过,但我非常确定它会:
_uploader.Upload(token, "Foo");
token.ThrowIfCancellationRequested();
token.ThrowIfCancellationRequested();
_uploader.Upload(token, "Bar");
如评论所述,最简单的解决方法是将token.ThrowIfCancellationRequested
调用推入Upload
调用中。 假设由于某种原因这不可行,则可以采用以下方法来测试您的场景。
首先,我会将检查取消请求是否已被请求并且未调用操作的功能封装到可测试的内容中。 初步考虑,此过程可能如下所示:
public interface IActionRunner {
void ExecIfNotCancelled(CancellationToken token, Action action);
}
public class ActionRunner : IActionRunner{
public void ExecIfNotCancelled(CancellationToken token, Action action) {
token.ThrowIfCancellationRequested();
action();
}
}
这可以通过两个测试轻松测试。一个是检查在令牌未被取消时是否调用了操作,另一个是验证在令牌被取消时是否不会调用操作。这些测试看起来像:
[TestMethod]
public void TestActionRunnerExecutesAction() {
bool run = false;
var runner = new ActionRunner();
var token = new CancellationToken();
runner.ExecIfNotCancelled(token, () => run = true);
Assert.AreEqual(true, run);
}
[TestMethod]
public void TestActionRunnerDoesNotExecuteIfCancelled() {
bool run = false;
var runner = new ActionRunner();
var token = new CancellationToken(true);
try {
runner.ExecIfNotCancelled(token, () => run = true);
Assert.Fail("Exception not thrown");
}
catch (OperationCanceledException) {
}
Assert.AreEqual(false, run);
}
我会将 IActionRunner
注入到 UploadEngine
中,并验证它是否被正确调用。因此,您的 PerformUpload
方法将变为:
public void PerformUpload(CancellationToken token) {
_actionRunner.ExecIfNotCancelled(token, () => _uploader.Upload(token, "Foo"));
_actionRunner.ExecIfNotCancelled(token, () => _uploader.Upload(token, "Bar"));
}
您可以编写一对测试来验证
PerformUpload
。第一个测试检查如果设置了一个
ActionRunner
模拟来执行提供的操作,则至少调用一次
Upload
。第二个测试验证如果
ActionRunner
模拟被设置为忽略该操作,则不会调用
Upload
。这基本上确保方法中
所有的
Upload
调用都是通过
ActionRunner
完成的。这些测试如下所示:
[TestMethod]
public void TestUploadCallsMadeThroughActionRunner() {
var uploader = new Mock<IUploader>();
var runner = new Mock<IActionRunner>();
var token = new CancellationToken();
int callCount = 0;
uploader.Setup(a => a.Upload(token, It.IsAny<string>())).Callback(() => callCount++);
runner.Setup(x => x.ExecIfNotCancelled(token, It.IsAny<Action>()))
.Callback<CancellationToken, Action>((tok,act)=>act());
var engine = new UploadEngine(uploader.Object, runner.Object);
engine.PerformUpload(token);
Assert.IsTrue(callCount > 0);
}
[TestMethod]
public void TestNoUploadCallsMadeThroughWithoutActionRunner() {
var uploader = new Mock<IUploader>();
var runner = new Mock<IActionRunner>();
var token = new CancellationToken();
int callCount = 0;
uploader.Setup(a => a.Upload(token, It.IsAny<string>())).Callback(() => callCount++);
runner.Setup(x => x.ExecIfNotCancelled(token, It.IsAny<Action>()))
.Callback<CancellationToken, Action>((tok, act) => { });
var engine = new UploadEngine(uploader.Object, runner.Object);
engine.PerformUpload(token);
Assert.AreEqual(0, callCount);
}
当然,您可能想为UploadEngine
编写其他测试,但它们似乎超出了当前问题的范围...