Skip to content

Commit b8d97b9

Browse files
IT-VBFKjnyrup
authored andcommitted
Guard methods against assertion scope [Not]HaveExplicit(Property|Method) (fluentassertions#2403)
* Guard against assertion scope: `[Not]HaveExplicitProperty` * Guard against assertion scope: `[Not]HaveExplicitMethod` * Add release notes
1 parent 5a2c704 commit b8d97b9

File tree

4 files changed

+128
-51
lines changed

4 files changed

+128
-51
lines changed

Src/FluentAssertions/Types/TypeAssertions.cs

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -462,22 +462,27 @@ public AndConstraint<TypeAssertions> Implement(Type interfaceType, string becaus
462462
{
463463
Guard.ThrowIfArgumentIsNull(interfaceType);
464464

465-
bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject;
466-
467-
Execute.Assertion
468-
.BecauseOf(because, becauseArgs)
469-
.WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType)
470-
.ForCondition(interfaceType.IsInterface)
471-
.FailWith(", but {0} is not an interface.", interfaceType)
472-
.Then
473-
.ForCondition(containsInterface)
474-
.FailWith(", but it does not.")
475-
.Then
476-
.ClearExpectation();
465+
AssertSubjectImplements(interfaceType, because, becauseArgs);
477466

478467
return new AndConstraint<TypeAssertions>(this);
479468
}
480469

470+
private bool AssertSubjectImplements(Type interfaceType, string because = "", params object[] becauseArgs)
471+
{
472+
bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject;
473+
474+
return Execute.Assertion
475+
.BecauseOf(because, becauseArgs)
476+
.WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType)
477+
.ForCondition(interfaceType.IsInterface)
478+
.FailWith(", but {0} is not an interface.", interfaceType)
479+
.Then
480+
.ForCondition(containsInterface)
481+
.FailWith(", but it does not.")
482+
.Then
483+
.ClearExpectation();
484+
}
485+
481486
/// <summary>
482487
/// Asserts that the current <see cref="Type"/> implements interface <typeparamref name="TInterface"/>.
483488
/// </summary>
@@ -973,15 +978,18 @@ public AndConstraint<TypeAssertions> HaveExplicitProperty(
973978

974979
if (success)
975980
{
976-
Subject.Should().Implement(interfaceType, because, becauseArgs);
977-
978-
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);
979-
980-
Execute.Assertion
981-
.BecauseOf(because, becauseArgs)
982-
.ForCondition(explicitlyImplementsProperty)
983-
.FailWith(
984-
$"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not.");
981+
success = AssertSubjectImplements(interfaceType, because, becauseArgs);
982+
983+
if (success)
984+
{
985+
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);
986+
987+
Execute.Assertion
988+
.BecauseOf(because, becauseArgs)
989+
.ForCondition(explicitlyImplementsProperty)
990+
.FailWith(
991+
$"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not.");
992+
}
985993
}
986994

987995
return new AndConstraint<TypeAssertions>(this);
@@ -1040,16 +1048,19 @@ public AndConstraint<TypeAssertions> NotHaveExplicitProperty(
10401048

10411049
if (success)
10421050
{
1043-
Subject.Should().Implement(interfaceType, because, becauseArgs);
1044-
1045-
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);
1046-
1047-
Execute.Assertion
1048-
.BecauseOf(because, becauseArgs)
1049-
.ForCondition(!explicitlyImplementsProperty)
1050-
.FailWith(
1051-
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" +
1052-
", but it does.");
1051+
success = AssertSubjectImplements(interfaceType, because, becauseArgs);
1052+
1053+
if (success)
1054+
{
1055+
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);
1056+
1057+
Execute.Assertion
1058+
.BecauseOf(because, becauseArgs)
1059+
.ForCondition(!explicitlyImplementsProperty)
1060+
.FailWith(
1061+
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" +
1062+
", but it does.");
1063+
}
10531064
}
10541065

10551066
return new AndConstraint<TypeAssertions>(this);
@@ -1111,16 +1122,19 @@ public AndConstraint<TypeAssertions> HaveExplicitMethod(
11111122

11121123
if (success)
11131124
{
1114-
Subject.Should().Implement(interfaceType, because, becauseArgs);
1115-
1116-
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);
1117-
1118-
Execute.Assertion
1119-
.BecauseOf(because, becauseArgs)
1120-
.ForCondition(explicitlyImplementsMethod)
1121-
.FailWith(
1122-
$"Expected {Subject} to explicitly implement {interfaceType}.{name}" +
1123-
$"({GetParameterString(parameterTypes)}){{reason}}, but it does not.");
1125+
success = AssertSubjectImplements(interfaceType, because, becauseArgs);
1126+
1127+
if (success)
1128+
{
1129+
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);
1130+
1131+
Execute.Assertion
1132+
.BecauseOf(because, becauseArgs)
1133+
.ForCondition(explicitlyImplementsMethod)
1134+
.FailWith(
1135+
$"Expected {Subject} to explicitly implement {interfaceType}.{name}" +
1136+
$"({GetParameterString(parameterTypes)}){{reason}}, but it does not.");
1137+
}
11241138
}
11251139

11261140
return new AndConstraint<TypeAssertions>(this);
@@ -1184,16 +1198,19 @@ public AndConstraint<TypeAssertions> NotHaveExplicitMethod(
11841198

11851199
if (success)
11861200
{
1187-
Subject.Should().Implement(interfaceType, because, becauseArgs);
1188-
1189-
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);
1190-
1191-
Execute.Assertion
1192-
.BecauseOf(because, becauseArgs)
1193-
.ForCondition(!explicitlyImplementsMethod)
1194-
.FailWith(
1195-
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}" +
1196-
$"({GetParameterString(parameterTypes)}){{reason}}, but it does.");
1201+
success = AssertSubjectImplements(interfaceType, because, becauseArgs);
1202+
1203+
if (success)
1204+
{
1205+
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);
1206+
1207+
Execute.Assertion
1208+
.BecauseOf(because, becauseArgs)
1209+
.ForCondition(!explicitlyImplementsMethod)
1210+
.FailWith(
1211+
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}" +
1212+
$"({GetParameterString(parameterTypes)}){{reason}}, but it does.");
1213+
}
11971214
}
11981215

11991216
return new AndConstraint<TypeAssertions>(this);

Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitMethod.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using FluentAssertions.Execution;
23
using Xunit;
34
using Xunit.Sdk;
45

@@ -183,6 +184,19 @@ public void When_asserting_a_type_has_an_explicit_method_with_an_empty_name_it_s
183184
act.Should().ThrowExactly<ArgumentException>()
184185
.WithParameterName("name");
185186
}
187+
188+
[Fact]
189+
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_not_implemented_at_all()
190+
{
191+
var act = () =>
192+
{
193+
using var _ = new AssertionScope();
194+
typeof(ClassWithMembers).Should().HaveExplicitMethod(typeof(IExplicitInterface), "Foo", new Type[0]);
195+
};
196+
197+
act.Should().Throw<XunitException>()
198+
.WithMessage("Expected type *ClassWithMembers* to*implement *IExplicitInterface, but it does not.");
199+
}
186200
}
187201

188202
public class HaveExplicitMethodOfT
@@ -440,6 +454,21 @@ public void When_asserting_a_type_does_not_have_an_explicit_method_with_an_empty
440454
act.Should().ThrowExactly<ArgumentException>()
441455
.WithParameterName("name");
442456
}
457+
458+
[Fact]
459+
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_implemented()
460+
{
461+
var act = () =>
462+
{
463+
using var _ = new AssertionScope();
464+
typeof(ClassExplicitlyImplementingInterface)
465+
.Should().NotHaveExplicitMethod(typeof(IExplicitInterface), "ExplicitMethod", new Type[0]);
466+
};
467+
468+
act.Should().Throw<XunitException>()
469+
.WithMessage("Expected *ClassExplicitlyImplementingInterface* to not*implement " +
470+
"*IExplicitInterface.ExplicitMethod(), but it does.");
471+
}
443472
}
444473

445474
public class NotHaveExplicitMethodOfT

Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitProperty.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using FluentAssertions.Execution;
23
using Xunit;
34
using Xunit.Sdk;
45

@@ -168,6 +169,19 @@ public void When_asserting_a_type_has_an_explicit_property_with_an_empty_name_it
168169
act.Should().ThrowExactly<ArgumentException>()
169170
.WithParameterName("name");
170171
}
172+
173+
[Fact]
174+
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_not_implemented_at_all()
175+
{
176+
var act = () =>
177+
{
178+
using var _ = new AssertionScope();
179+
typeof(int).Should().HaveExplicitProperty(typeof(IExplicitInterface), "Foo");
180+
};
181+
182+
act.Should().Throw<XunitException>()
183+
.WithMessage("Expected type System.Int32 to*implement *IExplicitInterface, but it does not.");
184+
}
171185
}
172186

173187
public class HaveExplicitPropertyOfT
@@ -395,6 +409,21 @@ public void When_asserting_a_type_does_not_have_an_explicit_property_with_an_emp
395409
act.Should().ThrowExactly<ArgumentException>()
396410
.WithParameterName("name");
397411
}
412+
413+
[Fact]
414+
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_implemented()
415+
{
416+
var act = () =>
417+
{
418+
using var _ = new AssertionScope();
419+
typeof(ClassExplicitlyImplementingInterface)
420+
.Should().NotHaveExplicitProperty(typeof(IExplicitInterface), "ExplicitStringProperty");
421+
};
422+
423+
act.Should().Throw<XunitException>()
424+
.WithMessage("Expected *ClassExplicitlyImplementingInterface* to*implement " +
425+
"*IExplicitInterface.ExplicitStringProperty, but it does.");
426+
}
398427
}
399428

400429
public class NotHaveExplicitPropertyOfT

docs/_pages/releases.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ sidebar:
2222
* Improved the failure message for `NotBeOfType` when wrapped in an `AssertionScope` and the subject is null - [#2399](https://github.com/fluentassertions/fluentassertions/pull/2399)
2323
* Improved the failure message for `BeWritable`/`BeReadable` when wrapped in an `AssertionScope` and the subject is read-only/write-only - [#2399](https://github.com/fluentassertions/fluentassertions/pull/2399)
2424
* Improved the failure message for `ThrowExactly[Async]` when wrapped in an `AssertionScope` and no exception is thrown - [#2398](https://github.com/fluentassertions/fluentassertions/pull/2398)
25+
* Improved the failure message for `[Not]HaveExplicitProperty` when wrapped in an `AssertionScope` and not implementing the interface - [#2403](https://github.com/fluentassertions/fluentassertions/pull/2403)
26+
* Improved the failure message for `[Not]HaveExplicitMethod` when wrapped in an `AssertionScope` and not implementing the interface - [#2403](https://github.com/fluentassertions/fluentassertions/pull/2403)
2527

2628

2729
## 6.12.0

0 commit comments

Comments
 (0)