diff --git a/README.md b/README.md index 88269dd..83b9992 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # 🚎 ReflectionEventing [Created with ❤ in Poland by lepo.co](https://dev.lepo.co/) -Unleash the power of decoupled design with eventing. ReflectionEventing empowers developers to create simple events between services using DI in WPF, WinForms, or CLI applications. By facilitating better Inversion of Control, ReflectionEventing helps reduce coupling, enhancing the modularity and flexibility of your applications. +ReflectionEventing is a powerful tool for developers looking to create decoupled designs in WPF, WinForms, or CLI applications. By leveraging the power of Dependency Injection (DI) and eventing, ReflectionEventing promotes better Inversion of Control (IoC), reducing coupling and enhancing the modularity and flexibility of your applications. [![GitHub license](https://img.shields.io/github/license/lepoco/reflectioneventing)](https://github.com/lepoco/reflectioneventing/blob/master/LICENSE) [![Nuget](https://img.shields.io/nuget/v/ReflectionEventing)](https://www.nuget.org/packages/ReflectionEventing/) [![Nuget](https://img.shields.io/nuget/dt/ReflectionEventing?label=nuget)](https://www.nuget.org/packages/ReflectionEventing/) [![Sponsors](https://img.shields.io/github/sponsors/lepoco)](https://github.com/sponsors/lepoco) ## 👀 What does this repo contain? -The repository contains NuGet package source code, which uses C# reflection to register services that can be used to listen for local events. +This repository houses the source code for the ReflectionEventing NuGet package. The package utilizes C# reflection to register services that can listen for and respond to local events. ## Gettings started @@ -28,25 +28,29 @@ dotnet add package ReflectionEventing.DependencyInjection NuGet\Install-Package ReflectionEventing.DependencyInjection ``` -### Usage +### 🛠️ How to Use ReflectionEventing -#### Register consumers and bus +#### 1. Register Consumers and the Event Bus + +In this step, we register our ViewModel as a singleton and add it as a consumer to the event bus. This allows the ViewModel to listen for events published on the bus. ```csharp IHost host = Host.CreateDefaultBuilder() .ConfigureServices((context, services) => { - _ = services.AddSingleton(); - _ = services.AddEventBus(e => + services.AddSingleton(); + services.AddEventBus(e => { - _ = e.AddConsumer(); + e.AddConsumer(); }); } ) .Build(); ``` -#### Publish your event +#### 2. Publish Events + +Here, we create a background service that publishes an event on the event bus. This event could be anything - in this case, we're publishing a `BackgroundTicked` event. ```csharp public class MyBackgroundService(IEventBus eventBus) @@ -58,7 +62,9 @@ public class MyBackgroundService(IEventBus eventBus) } ``` -#### Now you can listen for the event +#### 3. Listen for Events + +Finally, we implement the `IConsumer` interface in our ViewModel. This allows the ViewModel to consume `BackgroundTicked` events. When a `BackgroundTicked` event is published, the `ConsumeAsync` method is called, and we update the `CurrentTick` property. ```csharp public partial class MainWindowViewModel : ObservableObject, IConsumer @@ -77,7 +83,7 @@ public partial class MainWindowViewModel : ObservableObject, IConsumer> consumers = _eventBusBuilder.GetConsumers(); + _ = consumers.Should().ContainKey(consumerType); + } + + [Fact] + public void AddConsumer_ShouldAddEventTypeToConsumerInDictionary() + { + Type consumerType = typeof(MySampleConsumer); + + _eventBusBuilder.AddConsumer(consumerType); + + IDictionary> consumers = _eventBusBuilder.GetConsumers(); + _ = consumers[consumerType].Should().Contain(typeof(TestEvent)); + } + + public sealed record TestEvent; + + public sealed record MySampleConsumer : IConsumer + { + public Task ConsumeAsync(TestEvent payload, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/tests/ReflectionEventing.UnitTests/EventBusTests.cs b/tests/ReflectionEventing.UnitTests/EventBusTests.cs new file mode 100644 index 0000000..ef42a19 --- /dev/null +++ b/tests/ReflectionEventing.UnitTests/EventBusTests.cs @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors. +// All Rights Reserved. + +namespace ReflectionEventing.UnitTests; + +public sealed class EventBusTests +{ + private readonly IConsumerProvider _consumerProvider; + private readonly IConsumerTypesProvider _consumerTypesProvider; + private readonly EventBus _eventBus; + + public EventBusTests() + { + _consumerProvider = Substitute.For(); + _consumerTypesProvider = Substitute.For(); + _eventBus = new EventBus(_consumerProvider, _consumerTypesProvider); + } + + [Fact] + public async Task PublishAsync_ShouldCallConsumeAsyncOnAllConsumers() + { + TestEvent testEvent = new(); + Type consumerType = typeof(IConsumer); + IConsumer consumer = Substitute.For>(); + + _ = _consumerTypesProvider.GetConsumerTypes().Returns([consumerType]); + _ = _consumerProvider.GetConsumerTypes(consumerType).Returns([consumer]); + + await _eventBus.PublishAsync(testEvent, CancellationToken.None); + + await consumer.Received().ConsumeAsync(testEvent, Arg.Any()); + } + + [Fact] + public async Task Publish_ShouldCallPublishAsync() + { + TestEvent testEvent = new(); + Type consumerType = typeof(IConsumer); + IConsumer consumer = Substitute.For>(); + + _ = _consumerTypesProvider.GetConsumerTypes().Returns([consumerType]); + _ = _consumerProvider.GetConsumerTypes(consumerType).Returns([consumer]); + + _eventBus.Publish(testEvent); + + await consumer.Received().ConsumeAsync(testEvent, Arg.Any()); + } + + public sealed record TestEvent; +} diff --git a/tests/ReflectionEventing.UnitTests/GlobalUsings.cs b/tests/ReflectionEventing.UnitTests/GlobalUsings.cs new file mode 100644 index 0000000..40feee6 --- /dev/null +++ b/tests/ReflectionEventing.UnitTests/GlobalUsings.cs @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. +// Copyright (C) Leszek Pomianowski and ReflectionEventing Contributors. +// All Rights Reserved. + +global using NSubstitute; +global using System; +global using System.Collections.Generic; +global using System.Threading; +global using System.Threading.Tasks; +global using Xunit; diff --git a/tests/ReflectionEventing.UnitTests/ReflectionEventing.UnitTests.csproj b/tests/ReflectionEventing.UnitTests/ReflectionEventing.UnitTests.csproj new file mode 100644 index 0000000..fe99c83 --- /dev/null +++ b/tests/ReflectionEventing.UnitTests/ReflectionEventing.UnitTests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + false + true + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + +