I have a CancelScheduledJob method, taking in the Id of a backgroundjob and using the JobStorage to retrieve this Id and cancelling the matching hangfire job:
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));
Verifying on an Enqueue(), or Schedule() method is possible by verifying the Create method called in the backgroundJobClient mock, such as here:
_backgroundJobClientMock.Verify(x => x.Create(
It.Is<Job>(job => job.Method.Name == "Run" && Guid.Parse(job.Args[0].ToString()) == input),
It.IsAny<ScheduledState>()));
But how would I go about verifying the Delete method? I am already mocking the JobStorage, but cannot seem to find a way to verify the Delete() method. Currently I have this:
_backgroundJobClientMock.Verify(
x => x.Delete(It.Is<string>(jobId => jobId == "job1")),
Times.Once
);
But I run into the common issue that Delete is an extension method and cannot be used in a setup/verification expression.
You can do something "flaky" and test for the instance method that is called from the extension method - (current source code):
public static bool Delete(
[NotNull] this IBackgroundJobClient client,
[NotNull] string jobId,
[CanBeNull] string fromState) {
if (client == null) throw new ArgumentNullException(nameof(client));
var state = new DeletedState();
return client.ChangeState(jobId, state, fromState);
}
This would be ChangeState
, so Verify
against it.
EDIT:
If you cannot or (don't want to) wrap things in a custom interface in your actual code as the other answers suggest, you can check this answer of mine for an alternative approach. It was for verifying ILogger
s extension methods like LogInformation
, but it should work here too if you are depending on IBackgroundJobClient
. In a nutshell, You create a IBackgroundJobClientExt:IBackgroundJobClient
interface that includes the extension methods explicitly. Then use DispatchProxy
to create an instance of it and pass it to our sut
. That way you can still Verify
against Delete
in your testing code and maintain the connection between extension methods and instance methods they call in a single place - in the DispatchProxy
's Invoke
method.