Skip to content

Commit

Permalink
Do not add all arguments of type T to the matching events, if one i…
Browse files Browse the repository at this point in the history
…s found

This closes #1915
  • Loading branch information
ITaluone committed May 3, 2022
1 parent c33006c commit dd1d982
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 34 deletions.
42 changes: 13 additions & 29 deletions Src/FluentAssertions/EventRaisingExtensions.cs
Expand Up @@ -62,33 +62,25 @@ public static IEventRecording WithArgs<T>(this IEventRecording eventRecording, E

Func<T, bool> compiledPredicate = predicate.Compile();

bool hasArgumentOfRightType = false;
var eventsMatchingPredicate = new List<OccurredEvent>();

foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType<T>().ToArray();
if (typedParameters.Any())
{
hasArgumentOfRightType = true;
}

if (typedParameters.Any(parameter => compiledPredicate(parameter)))
{
eventsMatchingPredicate.Add(@event);
}
}

if (!hasArgumentOfRightType)
{
throw new ArgumentException("No argument of event " + eventRecording.EventName + " is of type <" + typeof(T) + ">.");
}
bool hasFoundArgumentsBefore = eventsMatchingPredicate.Any();

if (!eventsMatchingPredicate.Any())
{
Execute.Assertion
.FailWith("Expected at least one event with arguments matching {0}, but found none.", predicate.Body);
}
Execute.Assertion
.ForCondition(hasFoundArgumentsBefore)
.FailWith($"Expected at least one event with arguments matching either the type <{{0}}> or parameters {{1}}, but found none.",
typeof(T),
predicate.Body);

return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
}
Expand All @@ -110,18 +102,15 @@ public static IEventRecording WithArgs<T>(this IEventRecording eventRecording, p
foreach (OccurredEvent @event in eventRecording)
{
var typedParameters = @event.Parameters.OfType<T>().ToArray();
if (typedParameters.Any())
{
hasArgumentOfRightType = true;
}
hasArgumentOfRightType = typedParameters.Any();

if (predicates.Length > typedParameters.Length)
{
throw new ArgumentException(
$"Expected the event to have at least {predicates.Length} parameters of type {typeof(T)}, but only found {typedParameters.Length}.");
}

bool isMatch = true;
bool isMatch = true && hasArgumentOfRightType;
for (int index = 0; index < predicates.Length && isMatch; index++)
{
isMatch = compiledPredicates[index]?.Invoke(typedParameters[index]) ?? true;
Expand All @@ -133,18 +122,13 @@ public static IEventRecording WithArgs<T>(this IEventRecording eventRecording, p
}
}

if (!hasArgumentOfRightType)
{
throw new ArgumentException("No argument of event " + eventRecording.EventName + " is of type <" + typeof(T) + ">.");
}
bool hasFoundArgumentsBefore = eventsMatchingPredicate.Any();

if (!eventsMatchingPredicate.Any())
{
Execute
.Assertion
.FailWith("Expected at least one event with arguments matching {0}, but found none.",
Execute.Assertion
.ForCondition(hasFoundArgumentsBefore)
.FailWith($"Expected at least one event with arguments matching either the type <{{0}}> or parameters {{1}}, but found none.",
typeof(T),
string.Join(" | ", predicates.Where(p => p is not null).Select(p => p.Body.ToString())));
}

return new FilteredEventRecording(eventRecording, eventsMatchingPredicate);
}
Expand Down
161 changes: 156 additions & 5 deletions Tests/FluentAssertions.Specs/Events/EventAssertionSpecs.cs
Expand Up @@ -179,7 +179,7 @@ public void When_the_event_parameters_dont_match_it_should_throw()
act
.Should().Throw<XunitException>()
.WithMessage(
"Expected at least one event with arguments matching (args.PropertyName == \"SomeProperty\"), but found none.");
"Expected at least one event with arguments matching*type*PropertyChangedEventArgs*parameter*(args.PropertyName == \"SomeProperty\"), but found none.");
}

[Fact]
Expand All @@ -197,8 +197,8 @@ public void When_the_event_args_are_of_a_different_type_it_should_throw()

// Assert
act
.Should().Throw<ArgumentException>()
.WithMessage("No argument of event PropertyChanged is of type *CancelEventArgs>*");
.Should().Throw<XunitException>()
.WithMessage("Expected*event*argument*type*CancelEventArgs>*");
}

[Fact]
Expand Down Expand Up @@ -321,7 +321,7 @@ public void When_a_non_conventional_event_with_a_specific_argument_was_not_raise

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected at least one event with arguments matching (args == " + wrongArgument + "), but found none.");
"Expected at least one event with arguments matching*type*Int32*parameters*(args == " + wrongArgument + "), but found none.");
}

[Fact]
Expand All @@ -340,7 +340,7 @@ public void When_a_non_conventional_event_with_many_specific_arguments_was_not_r

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected at least one event with arguments matching \"(args == \"" + wrongArgument +
"Expected at least one event with arguments matching*\"(args == \"" + wrongArgument +
"\")\", but found none.");
}

Expand Down Expand Up @@ -808,6 +808,157 @@ public void When_monitoring_interface_with_inherited_event_it_should_not_throw()
}
}

public class GetRecordingsForWithArgs
{
[Fact]
public void One_matching_argument_type_before_non_matching_types_passes()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new B());
a.OnEvent(new C());

// Act / Assert
IEventRecording filteredEvents = aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>();
filteredEvents.Should().HaveCount(1);
}

[Fact]
public void One_matching_argument_type_after_non_matching_types_passes()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new B());

// Act / Assert
IEventRecording filteredEvents = aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>();
filteredEvents.Should().HaveCount(1);
}

[Fact]
public void None_matching_argument_type_with_different_expected_argument_type_fails()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new C());

// Act
Action act = () => aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>();

// Assert
act.Should().Throw<XunitException>()
.WithMessage("Expected*event*argument*");
}

[Fact]
public void One_matching_argument_type_anywhere_between_non_matching_types_passes()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new B());
a.OnEvent(new C());

// Act / Assert
IEventRecording filteredEvents = aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>();
filteredEvents.Should().HaveCount(1);
}

[Fact]
public void One_matching_argument_type_anywhere_between_non_matching_types_with_parameters_passes()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new B());
a.OnEvent(new C());

// Act / Assert
IEventRecording filteredEvents = aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>(b => true);
filteredEvents.Should().HaveCount(1);
}

[Fact]
public void None_matching_argument_types_with_one_parameter_matching_a_different_type_fails()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new C());

// Act
Action act = () => aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>(b => true);

// Assert
act.Should().Throw<XunitException>()
.WithMessage("Expected*event*argument*type*B*none*");
}

[Fact]
public void None_matching_argument_types_with_two_or_more_parameters_matching_a_different_type_fails()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new C());

// Act
Action act = () => aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>(b => true, b => false);

// Assert
act.Should().Throw<ArgumentException>()
.WithMessage("Expected*event*parameters*type*B*found*");
}

[Fact]
public void One_matching_argument_type_with_two_or_more_parameters_matching_a_different_type_fails()
{
// Arrange
A a = new A();
using var aMonitor = a.Monitor();

a.OnEvent(new C());
a.OnEvent(new B());

// Act
Action act = () => aMonitor.GetRecordingFor(nameof(A.Event)).WithArgs<B>(b => true, b => false);

// Assert
act.Should().Throw<ArgumentException>()
.WithMessage("Expected*event*parameters*type*B*found*");
}
}

public class A
{
public event EventHandler<object> Event;

public void OnEvent(object o)
{
Event.Invoke(nameof(A), o);
}
}

public class B { }

public class C { }

public class ClassThatRaisesEventsItself : IInheritsEventRaisingInterface
{
public event PropertyChangedEventHandler PropertyChanged;
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -18,6 +18,7 @@ sidebar:

### Fixes
* Fix the failure message for regex matches (occurrence overload) to include the missing subject - [#1913](https://github.com/fluentassertions/fluentassertions/pull/1913)
* Fixed `WithArgs` matching arguments with wrong type, if at least one argument type is matching - [#1920](https://github.com/fluentassertions/fluentassertions/pull/1920)

## 6.6.0

Expand Down

0 comments on commit dd1d982

Please sign in to comment.