From 9033879cfea01fc04c676036af9fc3659bb5a151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mira?= Date: Wed, 3 Aug 2022 19:04:06 +0100 Subject: [PATCH 1/3] persist event arguments for each call --- README.md | 17 ++++++++++++++++- src/EventTesting/EventHook.cs | 13 +++++++++++-- src/EventTesting/IEventHook.cs | 9 ++++++++- src/Test/SystemTest.cs | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8d7dcea..776da71 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 winthin 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: 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() From 24db901bbc01860839fd2eaf0e7df47e924eb3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mira?= Date: Fri, 5 Aug 2022 19:02:55 +0100 Subject: [PATCH 2/3] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 776da71..d17f422 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ 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 winthin a call, a list of event arguments `EventHook.CallsEventArgs` is saved. +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) From a8561460285e5aeb242307b4ac43df69e930a34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mira?= Date: Sun, 7 Aug 2022 00:12:39 +0100 Subject: [PATCH 3/3] add `Called.Never()` verifier --- README.md | 15 +++++++++++++++ src/EventTesting/Called.cs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index d17f422..21842bb 100644 --- a/README.md +++ b/README.md @@ -153,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);