I have tried using a mocking framework (Moq) but it doesn't work because Set is a non-overridable method. I then stupidly tried to make my own EventWaitHandle by extending the class. It doesn't seem that there is any point that I ca开发者_运维问答n hook in any code to detect if set was in fact called.
I want to do something similar to Timer.Dispose(WaitHandle notifyObject) where someone can pass in a wait handle and I will call set on it when something completes.
I would prefer to do this without having to create a wrapper class. It just seems like so much extra effort and work on both sides, mine and the person that has to use my class to create a wrapper.
This is unfortunately not possible. However, you are doing it the right way - Mocking away multithreading aspects of the code allowing tests to run single threaded. Tests then verifies that the correct methods (Set
in this case) are called.
I wish the .NET-framework was designed more with tests in mind allowing us to mock this type of method by letting the class implement a simple interface.
I've been in the same situation with this exact method and also some other ones that lack an interface. In the end I ended upp either A) writing a wrapper B) not writing a tests.
It's less than ideal, but an alternative approach is to create separate Tasks to test that a 'Set' call is made, one to do the real work, and one to block on a WaitHandle. Then the test becomes a case of waiting for both Tasks to complete (obviously adding in timeouts to prevent waiting forever!).
A very crude example could be as follows:
[Test]
public void Test_Blocking()
{
// Arrange
var ewh = new EventWaitHandle(false, EventResetMode.ManualReset);
System.Threading.Tasks.Task<int> blocker = BlockOn(ewh, 0);
// Act
System.Threading.Tasks.Task worker = Work(ewh);
var allTasks = new[] { worker, blocker };
while (!allTasks.All(t => t.IsCompleted))
{
// spin
}
// Assert
Assert.That(blocker.Result, Is.EqualTo(1));
}
private static System.Threading.Tasks.Task<int> BlockOn(EventWaitHandle waitHandle, int counter)
{
return System.Threading.Tasks.Task.Factory.StartNew<int>(() =>
{
waitHandle.WaitOne();
return counter + 1;
});
}
精彩评论