我有一个 CancelScheduledJob 方法,它接收后台作业的 Id 并使用 JobStorage 检索该 Id 并取消匹配的挂起作业:
var mon = JobStorage.Current.GetMonitoringApi();
var scheduledJobs = mon.ScheduledJobs(0, int.MaxValue);
var jobsToDelete = scheduledJobs.Where(job => job.Value.Job?.Args?.Any(arg => arg is Guid guid && guid == id) == true).ToList();
jobsToDelete?.ForEach(job => _backgroundJobClient.Delete(job.Key));
通过验证在 backgroundJobClient 模拟中调用的 Create 方法来验证 Enqueue() 或 Schedule() 方法,例如这里:
_backgroundJobClientMock.Verify(x => x.Create(
It.Is<Job>(job => job.Method.Name == "Run" && Guid.Parse(job.Args[0].ToString()) == input),
It.IsAny<ScheduledState>()));
但是我该如何验证 Delete 方法呢?我已经在模拟 JobStorage,但似乎找不到验证 Delete() 方法的方法。目前我有这个:
_backgroundJobClientMock.Verify(
x => x.Delete(It.Is<string>(jobId => jobId == "job1")),
Times.Once
);
但我遇到了一个常见问题,即 Delete 是一种扩展方法,不能在设置/验证表达式中使用。
您可以做一些“不寻常的”事情并测试从扩展方法调用的实例方法 - (当前源代码):
这将是
ChangeState
,非常Verify
反对的。编辑:
如果您不能或(不想)像其他答案所建议的那样在实际代码中将内容包装在自定义接口中,则可以查看我的这个答案以了解替代方法。它是用于验证
ILogger
像的扩展方法LogInformation
,但如果您依赖,它也应该在这里工作IBackgroundJobClient
。简而言之,您创建一个IBackgroundJobClientExt:IBackgroundJobClient
明确包含扩展方法的接口。然后使用DispatchProxy
创建它的一个实例并将其传递给我们的sut
。这样,您仍然可以在测试代码中Verify
反对并在一个地方 - 在的方法Delete
中 - 维护扩展方法和它们调用的实例方法之间的连接。DispatchProxy
Invoke
正如 Ivan Petrov 所写,当您知道扩展方法的实现方式时,您可以测试它。这使您能够根据扩展方法使用的底层 API 进行验证。
但它确实会导致测试变得脆弱。你的测试现在很容易受到第三方库更改的影响。
更好的选择是定义一个多态 API(接口或基类),该 API 是根据客户端代码的需求而不是实现细节定义的。在这里,我们将整个 Hangfire API 视为实现细节。
这更符合依赖倒置原则。你可以根据客户端代码的需求来定义编程的抽象,而不是根据第三方 API 提供的内容。
一旦定义了所需的API ,就可以使用实现细节来实现接口或基类:Hangfire API。
为了添加现有答案,尤其是马克的答案,您应该围绕
_backgroundJobClient
类型定义接口。类似于:
然后你就可以轻松地模拟
IBackgroundJobClientWrapper
接口。当然,您可以调整接口定义以更好地适合您的代码。