I am trying to write a Unit Test using JustMock that ignores an Event.
I do not want to test the Event itself, as it calls all sorts of internal functions that would require a massive amount of effort to Mock.
Here is a quick bit of example code:
public class Sample
{
public delegate void OneParameterFunction(int someVar);
public event OneParameterFunction TheEvent;
public Sample()
{
TheEvent += CallMe;
}
public void CallMe(int someVar)
{
Debug.WriteLine("CallMe was fired with parameter: " + someVar);
}
public void FireEvent()
{
// do stuff, business logic here...
if (TheEvent != null)
TheEvent(3);
}
}
And here is the test I would Love to write, but cannot:
[TestClass]
class EventMocking
{
[TestMethod]
public void DoNothingOnEvent()
{
var s = new Sample();
Mock.Arrange(() => s.TheEvent(Arg.AnyInt))
.DoNothing();
Mock.Arrange(() => s.CallMe(Arg.AnyInt))
.OccursNever();
s.FireEvent();
Mock.Assert(() => s.CallMe(Arg.AnyInt));
}
}
But I receive the following compiler error:
Error 1 The event 'Sample.TheEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Sample') C:\BizObjectTests\EventMocking.cs
Does anyone have any suggestions about how to stop an Event from propagating? I also do not want to Mock.Create<T> for a number of reasons, one being I would again, have to setup a lot more test data/objects.
It's impossible to mock the delegate invocation itself, since it's implemented internally by the JIT.
You have several alternative options. If you raise the event in a method dedicated for that purpose (as in your sample), then you can simply mock that method instead. So, in your sample that becomes:
Mock.Arrange(() => s.FireEvent()).DoNothing();
If that's not possible then you can mock the method that adds handlers to the event (the one called when Sample += ... is invoked). You can do this like so:
var mock = Mock.Create<Sample>(); // we need this just for the following arrangement
Mock.ArrangeSet(() => mock.TheEvent += null).IgnoreInstance().IgnoreArguments().DoNothing();
var real = new Sample(); // TheEvent += CallMe will now do nothing
real.FireEvent(); // TheEvent is empty
Finally, as a third option, you can remove all handlers from the event using reflection at some point where you know the event is just about to be fired, or that no one else will attach to it:
new PrivateAccessor(real).SetField("TheEvent", null);
real.FireEvent(); // TheEvent is null right now
Caveat: this last option is dependent on the compiler implementation. It will work for event declarations in C# code, but will not work for VB events.
Related
I have a method which is called from a 3rd party assembly and serves as our application-entry point. This method raises our event MyEvent. In order to ensure the same event-handler is only registered once and only once, I implemented my own logic for add and remove:
class MyEditorExtension
{
private EventHandler<MyArgs> myEvent
public EventHandler<MyArgs> MyEvent
{
add
{
if(this.myEvent == null || this.myEvent.GetInvocationList().All(x => !x.Equals(value))
this.myEvent += value;
}
remove { this.myEvent -= value; }
}
// this method is called from ArcMap
public void OnCreate()
{
...
MyEvent();
}
}
OnCreate is quite huge and can´t safely be refactored into smaller, testable units. However I want to check if my event-definition really does what it is supposed to do. In order to do so I tried to register the exact same method twice and check if it was executed twice:
[TestFixture]
public class ExtensionTest
{
int numCalls;
[Test]
public void Test_Register_Handler_Twice()
{
var target = new MyExtension();
target.MyEvent += myHandler;
target.MyEvent += myHandler;
target.MyEvent(new MyArgs());
Assert.AreEqual(1, this.numCalls);
}
private void(MyArgs args) { this.numCalls ++; }
}
Of course the above won´t compile because I can´t raise an event outside its class. I´d also like to avoid introducing a RaiseEvent-method solely for the sake of testing the event.
So I wonder if there´s any way to achieve this, e.g. using reflection?
Indeed, it is possible using reflection. However this is quite hacky. In fact this is a common problem when testing legacy-code as this one. This solution was inspired by this post: https://stackoverflow.com/a/12000050/2528063
You have to know that an event is nothing but an add- and remove-method around a private (hidden) delegate-field, just like a property is nothing but a get- and set-method around a private (also hidden) backing-field. Having said this you can access that delegate, which usually has the exact same name as your event:
var delegateField = typeof(MyExtension).GetField("MyEvent", BindingFlags.NonPublic)?.GetValue(target);
In our special case we have our own accessors and therefor the name of the delegate is provided directly within the source-cde of MyExtension. Thus we write this slightly different version:
var delegateField = typeof(MyExtension).GetField("myEvent", BindingFlags.NonPublic)?.GetValue(target);
Now we have our backing delegate, which we can easily invoke:
delegateField?.DynamicInvoke(new MyArgs());
Of course we should add some sanity-checks, e.g. for the case the delegate was renamed and thus couldn´t be found, but I guess you get the point.
I have structure as below. I want to test if LoadData is called when ViewLoaded event is triggered.
public interface ISetupView
{
event Action ViewLoaded;
}
public class BaseSetupController
{
private ISetupView view;
public BaseSetupController(ISetupView view)
{
this.view = view;
view.ViewLoaded += () => { LoadData(); };
}
public virtual void LoadData()
{
}
}
Currently I have test like below, but it is not working. It states that LoadData is never called.
[TestFixture]
public class BaseSetupControllerTests
{
[Test]
public void ViewLoad_LoadDataIsCalled()
{
Mock<ISetupView> view = new Mock<ISetupView>();
Mock<BaseSetupController> controller = new Mock<BaseSetupController>(view.Object);
controller.Setup(x => x.LoadData());
view.Raise(x => x.ViewLoaded += () => { });
controller.Verify(x=>x.LoadData(), Times.Once());
}
}
It seems that I just needed to create controller.Object before raising event:
var obj = controller.Object;
view.Raise(x=>x.ViewLoaded+=null);
Setting the event handler happens in the constructor, which isn't called if you're only mocking the object.
In a unit test, you have one concrete class. Its dependencies are what you'd mock.
Mocking them both basically only tests the mocking framework, not your class.
Since you want to test whether LoadData is called, you're probably interested if the event handler is set to LoadData. That LoadData is actually called when the event is raised is a given, unless you doubt the .NET framework itself.
This question discusses verifying whether an event has a specific subscriber. But it requires reflection and is not easy.
I'm trying to write a extension method of EventHandler, as followed:
public static class MyExtensions
{
public static void Add(this EventHandler handler, EventHandler next)
{
//Do something
}
}
It works fine for my events, such as:
public event EventHandler MyEvent
I can use my extension with no problem:
MyEvent.Add((x, y) => { /* do something */ });
But when I want to apply it to Click event of a TextBox, I get a compilation error:
error CS0079:
The event System.Windows.Forms.TextBoxBase.Click can only appear on the left hand side of += or -=
What puzzle me is, the Click event doesn't look any different from MyEvent, so why it does have the constraint?
If the Add method is called from within the same class that MyEvent is defined it'd probably work just fine.
However, the Click method is defined in a different class than where you are calling Add from. That's why it doesn't work.
The .NET framework doesn't allow events to be directly modified outside of the class that defined it otherwise a bit of rouge code could remove all of the event handlers of any class.
Here's the best extension method that I could come up with that will help you do what you want:
public static Action Add<TDelegate>(
this TDelegate handler,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler)
{
addHandler(handler);
return () => removeHandler(handler);
}
You can use it like this:
var bar = new Bar();
EventHandler handler = (s, e) =>
{
Console.WriteLine("MyEvent!");
};
var cleanup = handler.Add(h => bar.MyEvent +=h, h => bar.MyEvent -=h);
bar.Raise();
bar.Raise();
cleanup();
The cleanup action is used to remove the event handler when it is not needed.
I left out all of the required null-check code for simplicity. You would have to put them in in the appropriate places.
There is a difference between the two. A simple example can demonstrate it:
private EventHandler foodel;
public event EventHandler foo {
add { foodel += value; }
remove { foodel -= value; }
}
public event EventHandler bar;
...
foo.Add((s, e) => { }); // CS0079
bar.Add((s, e) => { }); // No error
Remove the accessors and the code compiles cleanly.
I'm going to go on a limb and say that you are unintentionally taking advantage of a compiler bug. It should have rejected the usage of an extension method on an event declared without accessors as well. The odds that Microsoft will fix the bug are very low, this has been in the wild for too long. The best workaround however is to delete the extension method.
Im trying to test a method which composes a collection of controls. It calls two methods:
Copies the original collection.
Sorts the new collection.
Ideally id like to be able to pass in a collection and test to see thats it sorts it correctly. Id also like to verify that method 1) is called twice, see below attempt based on the following:
Example using RhinoMock
The following test is producing errors when i try to create an instance of MainPresenter. General jist of the errors are "Can not convert from Moq.Mock to "FrazerMann.CsvImporter.UserInterface.IMainForm. + a similar one for IFileDialog.
[Test]
public void ComposeCollectionOfControls_CallSequence_4Calls()
{
var main = new Mock<IMainForm>();
var dialog = new Mock<IFileDialog>();
var temp = new Mock<IMainPresenter>();
temp.Setup(s => s.PopulateLists<Control>(It.IsAny<TableLayoutPanel>(), It.IsAny<List<Control>>()));
var testObject = new MainPresenter(main.Object, dialog.Object);
testObject.ComposeCollectionOfControls(It.IsAny<object>(), It.IsAny<EventArgs>());
temp.Verify(v => v.PopulateLists<Control>(It.IsAny<TableLayoutPanel>(), It.IsAny<List<Control>>()), Times.Once());
}
I would like to test the ComposeCollectionOfControls to ensure PopulateList() is called twice.
public interface IMainPresenter
{
void PopulateLists<T>(TableLayoutPanel userInputs, List<T> container) where T : Control;
int SortList<T>(T control1, T control2) where T : Control;
}
public class MainPresenter:IMainPresenter
{
UserInputEntity inputs;
IFileDialog _dialog;
IMainForm _view;
public MainPresenter(IMainForm view, IFileDialog dialog)
{
_view = view;
_dialog = dialog;
view.ComposeCollectionOfControls += ComposeCollectionOfControls;
view.SelectCsvFilePath += SelectCsvFilePath;
view.SelectErrorLogFilePath += SelectErrorLogFilePath;
view.DataVerification += DataVerification;
}
public void ComposeCollectionOfControls(object sender, EventArgs e)
{
PopulateLists<TextBox>(_view.ColumnNameCtrls, _view.SortedColumnNameCtrls);
_view.SortedColumnNameCtrls.Sort(SortList<TextBox>);
PopulateLists<ComboBox>(_view.ColumnDataTypeCtrls, _view.SortedColumnDataTypeCtrls);
_view.SortedColumnDataTypeCtrls.Sort(SortList<ComboBox>);
}
}
Could someone please give me some pointers as to how this should be done?
The error you are seeing is because your are passing the mock class itself (which is of type Moq.Mock) rather than the mocked object that Moq creates for you.
Instead of:
var testObject = new MainPresenter(main, dialog);
you need:
var testObject = new MainPresenter(main.Object, dialog.Object);
As an aside, it is usually considered bad practice to explicitly verify things like the number of calls made on a particular method. This leads to a tight coupling between your tests and a particular implementation, and consequently brittle tests.
By testing how many times you call a method you will often find a test failing after you refactor some code when the end result of the code is still correct.
It is much better to test the final state of the objects involved, and make your tests ignorant of how that state was reached.
I have code very similar to this but cannot work out how I test whether an event handler occured.
public class MyClass : MyAbstractClass
{
IFileSystem FileSystem;
public MyClass(IFileSystem myFileSys)
{
FileSystem = myFileSys;
FileSystem.EventHit += new EventHandler(FileSystem_EventHit);
}
public void FileSystem_EventHit(object sender, EventArgs e)
{
//Testing base.OnOutput is not possible which I wont go into
base.OnOutput("EventHit");
}
}
Testing code is here:
[Test]
public void DoSomething_WhenCalled_EventFired()
{
var mock = new Moq.Mock<IFileSystem>();
MyClass plugin = new MyClass (mock.Object);
mock.Object.DoSomething();
mock.Raise(x => x.EventHit += null, new EventArgs());
//Verify/Assert that MyClass handled and did something in the event handler
}
The simplest way I can think of is to just add your own handler in the test method, which should suffice I would think?
[Test]
public void DoSomething_WhenCalled_EventFired()
{
var mock = new Moq.Mock<IFileSystem>();
bool isHit = false;
mock.EventHit += (s, e) =>
{
isHit = true;
};
MyClass plugin = new MyClass (mock.Object);
mock.Object.DoSomething();
mock.Raise(x => x.EventHit += null, new EventArgs());
Assert.IsTrue(isHit);
}
As verifying something in the event handler would mean trying to test legacy code the option I went with was to test that the event fired from within the concrete type and not a mock.
[Test]
public void DoSomething_WhenCalled_EventFired()
{
FileSystem fs = new FileSystem(mock.Object, timerMock.Object);
bool WasItHit = false;
fs.EventHit += delegate { WasItHit = true; };
fs.DoSomething(); //This should call the event
Assert.IsTrue(WasItHit);
}
You need to inject a mock of whatever gets called as a result of the event handler invocation and verify it. Your comment says you can't test base base.OnOutput, but it seems to me that is exactly what you need to do.
Basically testing of a fact that method was called is not a valid test case, you should test a logic/behaviour behind a method. Obviously with a given event handler there is nothing to test, this is why a task looks not trivial.
Try out formulate in few words what are you trying to test, which test case. For instance
MyClass switches a state into the State==Hit whilst
FileSystem.EventHit event.
To do that you probably need a flag in MyClass indicating that event occured. I know, this will be just for purpose of running a test but sometimes is good to have something like that.