How to unit test code using SynchronizationContext?

I have the following code that I am trying to verify using NUnit and Rhino mocks.

void _tracker_IndexChanged(object sender, IndexTrackerChangedEventArgs e) { // _context is initialised as // SynchronizationContext _context = SynchronizationContext.Current; // _tracker.Index is stubbed to return the value 100 _context.Post(o => _view.SetTrackbarValue(_tracker.Index), null); } 

In the test case, I set the wait as

 _view.Expect(v => v.SetTrackbarValue(100)); 

and when I check the wait, unit test randomly comes out with a message

 Test(s) failed. Rhino.Mocks.Exceptions.ExpectationViolationException : IIndexTrackerView.SetTrackbarValue(100); Expected #1, Actual #0. 

I could not identify the problem here, how to fix it?

+4
source share
2 answers

I usually solve such problems by encapsulating the global state with an abstract class or interface that can be staged and mocked. Then, instead of directly accessing the global state, I add an instance of my abstract class or interface to the code that uses it.

This allows me to mock global behavior and makes it so that my tests are not dependent on or perform this unrelated behavior.

Here you can do it.

 public interface IContext { void Post(SendOrPostCallback d, Object state); } public class SynchronizationContextAdapter : IContext { private SynchronizationContext _context; public SynchronizationContextAdapter(SynchronizationContext context) { _context = context; } public virtual void Post(SendOrPostCallback d, Object state) { _context.Post(d, state); } } public class SomeClass { public SomeClass(IContext context) { _context = context; } void _tracker_IndexChanged(object sender, IndexTrackerChangedEventArgs e) { _context.Post(o => _view.SetTrackbarValue(_tracker.Index), null); } // ... } 

You can then make fun of or IContext so you don’t have to worry about streaming, and you can use a simple implementation that the delegate just does.

If I wrote unit tests to mock it, I would also write integration tests at a higher level that did not scoff at it, but had less granular checks.

+6
source

I haven't used Rhino Mocks after a while, so I can't remember the exact syntax, but if refactoring things as Merlin suggests is not an option, then another solution would be to use ManualResetEvent to wait for your layout to act on.

Something like that:

 [Test] public void ATest(){ ManualResetEvent completed = new ManualResetEvent(false); _view.Expect(v => v.SetTrackbarValue(100)).Do(() => completed.Set()); //Stuff done here... Assert.IsTrue(completed.WaitOne(1000), "Waited a second for a call that never arrived!"); } 

That way, you can wait until another thread triggers an event, after which you can continue. Make sure there is a reasonable timeout, so you do not wait forever, though!

+1
source

Source: https://habr.com/ru/post/1384281/


All Articles