chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
I need a property on a mocked object to return a value that depends upon the existance of observers of an event from the same mocked object. I've stumbled upon something that seems to work, but it isn't clean. How should this be done?

interface IObjectWithEvent {
  event Action<ObjectWithEvent> AnEvent;
  bool IsInStateX { get; };
} 


public class Observer {
  public Observer(IObjectWithEvent objWithEvent) {
    objWithEvent.AnEvent += EventHandler;
  }

  void EventHandler(ObjectWithEvent objWithEvent) {
    //here's a defect that I need to detect
    //the delegate should not be removed until
    // after querying IsInStateX

    objectWithEvent.AnEvent -= EventHandler;
    If (objWithEvent.IsInStateX) {
       // Do Something Important
    }
  }
}


[Test]
public void ObserverTest() {
  MockManager.Init();

  Mock<ObjectWithEvent> objWithEvent = 
     MockManager.MockObject<ObjectWithEvent>();

  MockedEvent mockedEvent = objWithEvent.ExpectAddEvent("AnEvent");

  Observer observer = new Observer(objWithEvent.MockedInstance);

  objWithEvent.ExpectGetAlways("IsInStateX",
    (DynamicReturnValue) delegate(object[] args, object obj) {
      //this seems to simulate the desired behavior, but it's ugly
      try {
         //this throws an exception when there are no observers
         valueUpdated.GetEventHandle();
         return true;
      }
      catch {
         return false;
      }
    });

  objWithEvent.ExpectRemoveEvent("AnEvent");

  mockedEvent.Fire(new object[] { objWithEvent.MockedInstance });

  MockManager.Verify();
}
asked by woodward (1.7k points)

2 Answers

0 votes
Hi

You are trying to test something that is really hard to test.
I think that once the test is depended on the order of calls it becomes fragile
and will fail from small changes in the system.

If the property IsInStateX should be called before you remove the event
maybe that logic should be inside the property?
In that case when the property is called in the test it will throw an exception
if there are no observers.

I might be wrong here since I don't know the code you're trying to test.
Please tell me if you need more help.
answered by ohad (35.5k points)
0 votes
Hi,
It should have been easy, but you have uncovered a bug
The test should have used mockedEvent.InstanceCount :

[Test,VerifyMocks]
public void ObserverTest() {
  Mock<ObjectWithEvent> objWithEvent = MockManager.MockObject<ObjectWithEvent>();

  MockedEvent mockedEvent = objWithEvent.ExpectAddEvent("AnEvent");
  objWithEvent.ExpectRemoveEvent("AnEvent");

  int numberOfRegisteredHandlers = 0;
  objWithEvent.ExpectGetAlways("IsInStateX",(DynamicReturnValue) delegate(object[] args, object obj) 
    {
      numberOfRegisteredHandlers  = mockedEvent.InstanceCount;
      return true;
    });


  Observer observer = new Observer(objWithEvent.MockedInstance);
  mockedEvent.Fire(objWithEvent.MockedInstance); 

  Assert.AreEqual(1,numberOfRegisteredHandlers);
}


But there is a bug and the mockedEvent.InstanceCount doesn't decrease when the event is removed. You have found a good workaround.

public bool HasListeners(MockedEvent mockedEvent)
{
    try 
    {
         mockedEvent.GetEventHandle();
         return true;
    }
    catch (TypeMockException e)
    {
         return false;
    }
}


Now we can test:
[Test,VerifyMocks]
public void Observer_WhenIsInStateXIsCalled_EventIsStillRegistered() {
  // Create a fake and expect event to be added and removed
  Mock<ObjectWithEvent> objWithEvent = MockManager.MockObject<ObjectWithEvent>();
  MockedEvent mockedEvent = objWithEvent.ExpectAddEvent("AnEvent");
  objWithEvent.ExpectRemoveEvent("AnEvent");

  // when in IsInStateX grab the event and see if anyone is listening
  bool hasRegisteredHandlers = false;
  objWithEvent.ExpectGetAlways("IsInStateX",(DynamicReturnValue) delegate(object[] args, object obj) 
    {
      hasRegisteredHandlers = HasListeners(mockedEvent);
      return true;
    });

  // run the code and fire the event
  Observer observer = new Observer(objWithEvent.MockedInstance);
  mockedEvent.Fire(objWithEvent.MockedInstance); 

  // was the event still registered in IsInStateX?
  Assert.IsTrue(hasRegisteredHandlers );
}


Tips:
:idea: The name of the test method has more meaning
:idea: Use [VerifyMocks] to make sure that the mocks are reset even if the test fails
:idea: Do all the setting up in the beginning and verify at the end to highlight the state being verified
answered by eli (5.7k points)
...