Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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<T, TEventArgs>.CallsEventArgs` is saved.

```cs
var hook = EventHook.For(obj)
.HookOnly<TestEventArgs>((o, h) => o.OnTest += h) as EventHook<TestObject, TestEventArgs>;
// or .Hook<TestEventArgs>((o, h) => o.OnTest += h).Build() as EventHook<TestObject, TestEventArgs>

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:
Expand Down Expand Up @@ -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());
```
5 changes: 5 additions & 0 deletions src/EventTesting/Called.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 11 additions & 2 deletions src/EventTesting/EventHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task WaitForCall(Func<Task> invocationAction)
await Task.Delay(TimeSpan.FromMilliseconds(50));
}

public void Reset()
public virtual void Reset()
{
Calls = 0;
}
Expand All @@ -46,8 +46,16 @@ public static EventHookBuilder<T> For<T>(T target)
}
}

internal class EventHook<T, TEventArgs> : EventHook
public class EventHook<T, TEventArgs> : EventHook, IEventHook<T, TEventArgs>
{
public List<TEventArgs> CallsEventArgs { get; protected set; } = new List<TEventArgs>();

public override void Reset()
{
CallsEventArgs.Clear();
base.Reset();
}

internal void SetEventName(string eventName)
{
EventName = eventName;
Expand All @@ -61,6 +69,7 @@ internal void AddVerification(Action<T, TEventArgs> validator)
internal void HandleEvent(object o, TEventArgs e)
{
++Calls;
CallsEventArgs.Add(e);

var i = 0;

Expand Down
9 changes: 8 additions & 1 deletion src/EventTesting/IEventHook.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
namespace EventTesting
using System.Collections.Generic;

namespace EventTesting
{
public interface IEventHook
{
string EventName { get; }

int Calls { get; }
}

public interface IEventHook<T, TEventArgs>
{
List<TEventArgs> CallsEventArgs { get; }
}
}
34 changes: 34 additions & 0 deletions src/Test/SystemTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class TestObject
{
public event EventHandler OnTest;
public event EventHandler<bool> OnCustomArgsTest;
public event EventHandler<TestEventArgs> OnComplexCustomArgsTest;

public void InvokeEvent()
{
Expand All @@ -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;
}
}


Expand Down Expand Up @@ -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<TestEventArgs>((obj, m) => obj.OnComplexCustomArgsTest += m)
.Build() as EventHook<TestObject, TestEventArgs>;

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()
Expand Down