Add unit tests for ForwardingEvent#25
Conversation
Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Hissal
left a comment
There was a problem hiding this comment.
Check comments and apply in all relevant events.
Substitution preferred to using concrete types.
|
|
||
| [Fact] | ||
| public void Publish_WithPublishFlag_ForwardsMessageToTarget() { | ||
| var target = new DisposableEvent<int>(); |
There was a problem hiding this comment.
Would it maybe be better to use a substitute IDisposableEvent and check if it received a Publish call instead of relying on the concrete DisposableEvent working.
There was a problem hiding this comment.
Updated to use Substitute.For<IDisposableEvent<int>>() and verify Publish calls directly on the substitute. Changes in commit 5bc2d2a.
| var target = new DisposableEvent<int>(); | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
| target.Subscribe(handler); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Subscribe); | ||
| sut.Publish(c_message); | ||
|
|
||
| handler.DidNotReceive().Handle(Arg.Any<int>()); |
There was a problem hiding this comment.
Would it maybe be better to use a substitute IDisposableEvent and check if it received a Publish call instead of relying on the concrete DisposableEvent working.
There was a problem hiding this comment.
Updated to use Substitute.For<IDisposableEvent<int>>() and verify Publish calls directly on the substitute. Changes in commit 5bc2d2a.
| target.Subscribe(new EventHandler<int>(_ => receivedMessages.Add("target"))); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Publish | ForwardFlags.IncludeSelf); | ||
| sut.Subscribe(new EventHandler<int>(_ => receivedMessages.Add("self"))); |
There was a problem hiding this comment.
Consider if using a substitute event handler and adding a Do callback to Handle would be better. If too much clutter using EventHandler is okish since it is so simple and can be assumed to work but prefer substitutes when feasible.
There was a problem hiding this comment.
Infact no eventhandlers at all would be preferred and rather use substitute IDisposableEvent and add a callback to receiving The Publish call.
There was a problem hiding this comment.
Refactored to use substitute IDisposableEvent with When/Do callbacks on Publish to track call order, eliminating the need for EventHandler instances. Changes in commit 5bc2d2a.
| // With AfterSelf timing, when subscribing, it should: | ||
| // 1. Subscribe to self's core first | ||
| // 2. Then subscribe to target | ||
| var handler = new EventHandler<int>(_ => callOrder.Add("handler")); | ||
| sut.Subscribe(handler); | ||
|
|
||
| // When target publishes, the handler (subscribed to target) should be called | ||
| target.Publish(c_message); |
There was a problem hiding this comment.
Probably shouldnt rely on Publishing when testing Subscription behaviour insteaad register When Do callbacks to substitutes to track callOrder.
There was a problem hiding this comment.
Updated to use When/Do callbacks on substitute IDisposableEvent.Subscribe() to track subscription order instead of relying on Publishing. Changes in commit 5bc2d2a.
…ng tests Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Applied the feedback to use substitutes instead of concrete types throughout the ForwardingEvent tests. Changes in commit 5bc2d2a. |
Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Hissal
left a comment
There was a problem hiding this comment.
Check comments.
Still need to use substitutes instead of concrete types in some tests.
If a "functional substitutet" is needed in many places consider making a method to create one (a substitute that uses a list to store handlers and then iterates ofer a copy of that list to avoid editing during iteration then call handle on all handlers)
|
|
||
| [Fact] | ||
| public void Publish_WithIncludeSelfFlag_SendsMessageToOwnHandlers() { | ||
| var target = new DisposableEvent<int>(); |
There was a problem hiding this comment.
even though the target is not used here still prefer to use a substitute
There was a problem hiding this comment.
Changed to use Substitute.For<IDisposableEvent<int>>() in commit 735c14a.
| [Fact] | ||
| public void Subscribe_WithAfterSelfTiming_SubscribesInCorrectOrder() { | ||
| var callOrder = new List<string>(); | ||
| var target = Substitute.For<IDisposableEvent<int>>(); | ||
| target.When(t => t.Subscribe(Arg.Any<IEventHandler<int>>())).Do(_ => callOrder.Add("target")); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Subscribe | ForwardFlags.IncludeSelf); | ||
|
|
||
| // With AfterSelf timing, when subscribing, it should: | ||
| // 1. Subscribe to self's core first | ||
| // 2. Then subscribe to target | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
| sut.Subscribe(handler); | ||
|
|
||
| // Target should have been subscribed to after self | ||
| callOrder.Should().Equal("target"); | ||
| target.Received(1).Subscribe(Arg.Any<IEventHandler<int>>()); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Subscribe_WithBeforeSelfTiming_SubscribesInCorrectOrder() { | ||
| var callOrder = new List<string>(); | ||
| var target = Substitute.For<IDisposableEvent<int>>(); | ||
| target.When(t => t.Subscribe(Arg.Any<IEventHandler<int>>())).Do(_ => callOrder.Add("target")); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.BeforeSelf, ForwardFlags.Subscribe | ForwardFlags.IncludeSelf); | ||
|
|
||
| // With BeforeSelf timing, when subscribing, it should: | ||
| // 1. Subscribe to target first | ||
| // 2. Then subscribe to self's core | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
| sut.Subscribe(handler); | ||
|
|
||
| // Target should have been subscribed to before self | ||
| callOrder.Should().Equal("target"); | ||
| target.Received(1).Subscribe(Arg.Any<IEventHandler<int>>()); | ||
| } |
There was a problem hiding this comment.
Maybe check sut.HandlerCount in the target.When().Do() call since right now these arent actually testing the order of operations.
There was a problem hiding this comment.
Updated Subscribe timing tests to check sut.HandlerCount within target.When().Do() callbacks to verify the order of operations. Changes in commit 735c14a.
|
|
||
| [Fact] | ||
| public void ForwardFlags_PubSub_CombinesPublishAndSubscribe() { | ||
| var target = new DisposableEvent<int>(); |
There was a problem hiding this comment.
Couldnt we check that a substitute target received Subscribe and publish with the specific arguments instead of using handlers and a concrete event?
There was a problem hiding this comment.
Refactored to use CreateFunctionalSubstitute<int>() which maintains a handler list and properly implements Subscribe/Publish behavior. Changes in commit 735c14a.
|
|
||
| [Fact] | ||
| public void ForwardFlags_AllWithSelf_CombinesAllFlagsIncludingSelf() { | ||
| var target = new DisposableEvent<int>(); |
There was a problem hiding this comment.
Maybe use a substitute and register calls in subscribe to add handler to a list and when publishing iterata a copy of list and call handle to avoid tests faling if the concrete implementation is broken.
There was a problem hiding this comment.
Created CreateFunctionalSubstitute<T>() helper method that uses a list to store handlers and iterates over a copy when publishing. Applied to this test in commit 735c14a.
|
|
||
| [Fact] | ||
| public void ForwardFlags_AllWithoutSelf_CombinesAllFlagsExceptSelf() { | ||
| var target = new DisposableEvent<int>(); |
There was a problem hiding this comment.
Maybe use a substitute and register calls in subscribe to add handler to a list and when publishing iterata a copy of list and call handle to avoid tests faling if the concrete implementation is broken.
There was a problem hiding this comment.
Changed to use CreateFunctionalSubstitute<int>() helper which manages handlers in a list. Changes in commit 735c14a.
| // ----- Standard Event Behavior Tests ----- // | ||
|
|
||
| [Fact] | ||
| public void IsDisposed_ReturnsFalse_WhenNotDisposed() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target); | ||
|
|
||
| sut.IsDisposed.Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void IsDisposed_ReturnsTrue_AfterDispose() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target); | ||
|
|
||
| sut.Dispose(); | ||
|
|
||
| sut.IsDisposed.Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Dispose_IsIdempotent() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Dispose); | ||
|
|
||
| sut.Dispose(); | ||
| sut.Dispose(); | ||
|
|
||
| target.IsDisposed.Should().BeTrue(); | ||
| sut.IsDisposed.Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void HandlerCount_ReturnsZero_WithoutIncludeSelf() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Subscribe); | ||
|
|
||
| sut.HandlerCount.Should().Be(0); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void HandlerCount_ReturnsCorrectCount_WithIncludeSelf() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.IncludeSelf); | ||
|
|
||
| sut.Subscribe(Substitute.For<IEventHandler<int>>()); | ||
| sut.Subscribe(Substitute.For<IEventHandler<int>>()); | ||
|
|
||
| sut.HandlerCount.Should().Be(2); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Publish_AfterDispose_DoesNotPublish() { | ||
| var target = new DisposableEvent<int>(); | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
| target.Subscribe(handler); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Publish); | ||
| sut.Dispose(); | ||
| sut.Publish(c_message); | ||
|
|
||
| handler.DidNotReceive().Handle(Arg.Any<int>()); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void Subscribe_AfterDispose_ReturnsEmptyDisposable() { | ||
| var target = new DisposableEvent<int>(); | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Subscribe | ForwardFlags.IncludeSelf); | ||
| sut.Dispose(); | ||
| var subscription = sut.Subscribe(handler); | ||
|
|
||
| subscription.Should().NotBeNull(); | ||
| sut.Publish(c_message); | ||
| handler.DidNotReceive().Handle(Arg.Any<int>()); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void ClearHandlers_AfterDispose_DoesNotThrow() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target); | ||
|
|
||
| sut.Dispose(); | ||
|
|
||
| var act = () => sut.ClearHandlers(); | ||
| act.Should().NotThrow(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SnapshotHandlers_WithoutIncludeSelf_ReturnsEmpty() { | ||
| var target = new DisposableEvent<int>(); | ||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.Subscribe); | ||
|
|
||
| using var snapshot = sut.SnapshotHandlers(); | ||
|
|
||
| snapshot.Span.Length.Should().Be(0); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void SnapshotHandlers_WithIncludeSelf_ReturnsHandlers() { | ||
| var target = new DisposableEvent<int>(); | ||
| var handler = Substitute.For<IEventHandler<int>>(); | ||
|
|
||
| var sut = new ForwardingEvent<int>(target, ForwardTiming.AfterSelf, ForwardFlags.IncludeSelf); | ||
| sut.Subscribe(handler); | ||
|
|
||
| using var snapshot = sut.SnapshotHandlers(); | ||
|
|
||
| snapshot.Span.Length.Should().Be(1); | ||
| snapshot.Span[0].Should().Be(handler); | ||
| } |
There was a problem hiding this comment.
use Substitute event instead of concrete.
There was a problem hiding this comment.
Replaced all concrete DisposableEvent instances with Substitute.For<IDisposableEvent<int>>() in the standard behavior tests section. Changes in commit 735c14a.
…substitute helper Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Applied all feedback. Created |
Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
ForwardingEvent Unit Tests - Complete ✅
Successfully implemented comprehensive unit tests for ForwardingEvent and related functionality with all substitutes.
Final Status
CreateFunctionalSubstitute<T>()helper methodDisposableEvent<int>()instances with substitutessut.HandlerCountin callbacksSummary
Key Changes from Feedback
sut.HandlerCountin callbacks to verify orderDisposableEventwithCreateFunctionalSubstituteCreateFunctionalSubstitute<T>()helperTest Classes
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.