diff --git a/README.md b/README.md index 8d7dcea..21842bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Event Testing [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build status](https://ci.appveyor.com/api/projects/status/0wckkllo1i5n8c49?svg=true)](https://ci.appveyor.com/project/f-tischler/eventtesting) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=f-tischler_EventTesting&metric=alert_status)](https://sonarcloud.io/dashboard?id=f-tischler_EventTesting) -Writing test code for event-driven APIs in C# involves a lot of boiler plate code which obfuscates tests. This library intents to alleviate this problem by provding a fluent programming model for verifying event invocations and valdidating event arguments. It also supports use cases where events may be fired asynchonously with some delay by allowing to specify a timeout for the invocation verification. +Writing test code for event-driven APIs in C# involves a lot of boiler plate code which obfuscates tests. This library intents to alleviate this problem by provding a fluent programming model for verifying event invocations and validating event arguments. It also supports use cases where events may be fired asynchonously with some delay by allowing to specify a timeout for the invocation verification. # Installation @@ -39,6 +39,21 @@ Verification is implemented using the `EventTesting.IVerifier` interface and can The class `EventTesting.Called` provides a simplified interface for creating verifiers to build a more fluent API. +In case you have multiple events being fired within a call, a list of event arguments `EventHook.CallsEventArgs` is saved. + +```cs +var hook = EventHook.For(obj) + .HookOnly((o, h) => o.OnTest += h) as EventHook; + // or .Hook((o, h) => o.OnTest += h).Build() as EventHook + +o.InvokeComplexCustomArgEvent(new TestEventArgs("event #99")); +o.InvokeComplexCustomArgEvent(new TestEventArgs("event #0")); + +Assert.AreEqual(2, hook.CallsEventArgs.Count); +Assert.AreEqual("event #99", hook.CallsEventArgs[0].Arg); +Assert.AreEqual("event #0", hook.CallsEventArgs[1].Arg); +``` + ## Argument Verification To test arguments passed to event handlers, verification actions can be registered e.g. to assert that the sender is not null: @@ -138,3 +153,18 @@ obj.InvokeEvent(); hook.Verify(Called.AtMost(2)); ``` + +### Never Raised + +```cs +using EventTesting; + +var obj = new TestObject(); + +var hook = EventHook.For(obj) + .HookOnly((o, h) => o.OnTest += h); + +obj.DoNotInvokeEvent(); + +hook.Verify(Called.Never()); +``` diff --git a/src/EventTesting/Called.cs b/src/EventTesting/Called.cs index 0fcc5e2..694c182 100644 --- a/src/EventTesting/Called.cs +++ b/src/EventTesting/Called.cs @@ -12,6 +12,11 @@ public static IVerifier Exactly(int times) return new ExactVerifier(times); } + public static IVerifier Never() + { + return Exactly(0); + } + public static IVerifier Once() { return Exactly(1); diff --git a/src/EventTesting/EventHook.cs b/src/EventTesting/EventHook.cs index b21dee1..f9bc048 100644 --- a/src/EventTesting/EventHook.cs +++ b/src/EventTesting/EventHook.cs @@ -35,7 +35,7 @@ public async Task WaitForCall(Func invocationAction) await Task.Delay(TimeSpan.FromMilliseconds(50)); } - public void Reset() + public virtual void Reset() { Calls = 0; } @@ -46,8 +46,16 @@ public static EventHookBuilder For(T target) } } - internal class EventHook : EventHook + public class EventHook : EventHook, IEventHook { + public List CallsEventArgs { get; protected set; } = new List(); + + public override void Reset() + { + CallsEventArgs.Clear(); + base.Reset(); + } + internal void SetEventName(string eventName) { EventName = eventName; @@ -61,6 +69,7 @@ internal void AddVerification(Action validator) internal void HandleEvent(object o, TEventArgs e) { ++Calls; + CallsEventArgs.Add(e); var i = 0; diff --git a/src/EventTesting/IEventHook.cs b/src/EventTesting/IEventHook.cs index e81c589..e95dc38 100644 --- a/src/EventTesting/IEventHook.cs +++ b/src/EventTesting/IEventHook.cs @@ -1,4 +1,6 @@ -namespace EventTesting +using System.Collections.Generic; + +namespace EventTesting { public interface IEventHook { @@ -6,4 +8,9 @@ public interface IEventHook int Calls { get; } } + + public interface IEventHook + { + List CallsEventArgs { get; } + } } \ No newline at end of file diff --git a/src/Test/SystemTest.cs b/src/Test/SystemTest.cs index a886250..e0bd99d 100644 --- a/src/Test/SystemTest.cs +++ b/src/Test/SystemTest.cs @@ -12,6 +12,7 @@ class TestObject { public event EventHandler OnTest; public event EventHandler OnCustomArgsTest; + public event EventHandler OnComplexCustomArgsTest; public void InvokeEvent() { @@ -24,6 +25,22 @@ public void InvokeCustomArgEvent() Assert.IsNotNull(OnCustomArgsTest); OnCustomArgsTest.Invoke(this, true); } + + public void InvokeComplexCustomArgEvent(TestEventArgs testEventArgs) + { + Assert.IsNotNull(OnComplexCustomArgsTest); + OnComplexCustomArgsTest.Invoke(this, testEventArgs); + } + } + + private class TestEventArgs : EventArgs + { + public string Arg { get; } + + public TestEventArgs(string arg) + { + Arg = arg; + } } @@ -72,6 +89,23 @@ public void TestOneInvocation() Assert.AreEqual(1, hook.Calls); } + [TestMethod] + public void TestMultipleInvocationWithComplexCustomArgs() + { + var o = new TestObject(); + var hook = EventTesting.EventHook.For(o) + .Hook((obj, m) => obj.OnComplexCustomArgsTest += m) + .Build() as EventHook; + + o.InvokeComplexCustomArgEvent(new TestEventArgs("event #99")); + o.InvokeComplexCustomArgEvent(new TestEventArgs("event #0")); + + Assert.AreEqual(2, hook.Calls); + Assert.AreEqual(2, hook.CallsEventArgs.Count); + Assert.AreEqual("event #99", hook.CallsEventArgs[0].Arg); + Assert.AreEqual("event #0", hook.CallsEventArgs[1].Arg); + } + [TestMethod] [ExpectedException(typeof(VerificationException))] public void TestVerifyOnceActuallyZero()