Skip to content
Merged
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
119 changes: 68 additions & 51 deletions Src/FluentAssertions/Types/TypeAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,22 +462,27 @@ public AndConstraint<TypeAssertions> Implement(Type interfaceType, string becaus
{
Guard.ThrowIfArgumentIsNull(interfaceType);

bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject;

Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType)
.ForCondition(interfaceType.IsInterface)
.FailWith(", but {0} is not an interface.", interfaceType)
.Then
.ForCondition(containsInterface)
.FailWith(", but it does not.")
.Then
.ClearExpectation();
AssertSubjectImplements(interfaceType, because, becauseArgs);

return new AndConstraint<TypeAssertions>(this);
}

private bool AssertSubjectImplements(Type interfaceType, string because = "", params object[] becauseArgs)
{
bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject;

return Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType)
.ForCondition(interfaceType.IsInterface)
.FailWith(", but {0} is not an interface.", interfaceType)
.Then
.ForCondition(containsInterface)
.FailWith(", but it does not.")
.Then
.ClearExpectation();
}

/// <summary>
/// Asserts that the current <see cref="Type"/> implements interface <typeparamref name="TInterface"/>.
/// </summary>
Expand Down Expand Up @@ -973,15 +978,18 @@ public AndConstraint<TypeAssertions> HaveExplicitProperty(

if (success)
{
Subject.Should().Implement(interfaceType, because, becauseArgs);

var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(explicitlyImplementsProperty)
.FailWith(
$"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not.");
success = AssertSubjectImplements(interfaceType, because, becauseArgs);

if (success)
{
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(explicitlyImplementsProperty)
.FailWith(
$"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not.");
}
}

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

if (success)
{
Subject.Should().Implement(interfaceType, because, becauseArgs);

var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!explicitlyImplementsProperty)
.FailWith(
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" +
", but it does.");
success = AssertSubjectImplements(interfaceType, because, becauseArgs);

if (success)
{
var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!explicitlyImplementsProperty)
.FailWith(
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" +
", but it does.");
}
}

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

if (success)
{
Subject.Should().Implement(interfaceType, because, becauseArgs);

var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(explicitlyImplementsMethod)
.FailWith(
$"Expected {Subject} to explicitly implement {interfaceType}.{name}" +
$"({GetParameterString(parameterTypes)}){{reason}}, but it does not.");
success = AssertSubjectImplements(interfaceType, because, becauseArgs);

if (success)
{
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(explicitlyImplementsMethod)
.FailWith(
$"Expected {Subject} to explicitly implement {interfaceType}.{name}" +
$"({GetParameterString(parameterTypes)}){{reason}}, but it does not.");
}
}

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

if (success)
{
Subject.Should().Implement(interfaceType, because, becauseArgs);

var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!explicitlyImplementsMethod)
.FailWith(
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}" +
$"({GetParameterString(parameterTypes)}){{reason}}, but it does.");
success = AssertSubjectImplements(interfaceType, because, becauseArgs);

if (success)
{
var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes);

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!explicitlyImplementsMethod)
.FailWith(
$"Expected {Subject} to not explicitly implement {interfaceType}.{name}" +
$"({GetParameterString(parameterTypes)}){{reason}}, but it does.");
}
}

return new AndConstraint<TypeAssertions>(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using FluentAssertions.Execution;
using Xunit;
using Xunit.Sdk;

Expand Down Expand Up @@ -183,6 +184,19 @@ public void When_asserting_a_type_has_an_explicit_method_with_an_empty_name_it_s
act.Should().ThrowExactly<ArgumentException>()
.WithParameterName("name");
}

[Fact]
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_not_implemented_at_all()
{
var act = () =>
{
using var _ = new AssertionScope();
typeof(ClassWithMembers).Should().HaveExplicitMethod(typeof(IExplicitInterface), "Foo", new Type[0]);
};

act.Should().Throw<XunitException>()
.WithMessage("Expected type *ClassWithMembers* to*implement *IExplicitInterface, but it does not.");
}
}

public class HaveExplicitMethodOfT
Expand Down Expand Up @@ -440,6 +454,21 @@ public void When_asserting_a_type_does_not_have_an_explicit_method_with_an_empty
act.Should().ThrowExactly<ArgumentException>()
.WithParameterName("name");
}

[Fact]
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_implemented()
{
var act = () =>
{
using var _ = new AssertionScope();
typeof(ClassExplicitlyImplementingInterface)
.Should().NotHaveExplicitMethod(typeof(IExplicitInterface), "ExplicitMethod", new Type[0]);
};

act.Should().Throw<XunitException>()
.WithMessage("Expected *ClassExplicitlyImplementingInterface* to not*implement " +
"*IExplicitInterface.ExplicitMethod(), but it does.");
}
}

public class NotHaveExplicitMethodOfT
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using FluentAssertions.Execution;
using Xunit;
using Xunit.Sdk;

Expand Down Expand Up @@ -168,6 +169,19 @@ public void When_asserting_a_type_has_an_explicit_property_with_an_empty_name_it
act.Should().ThrowExactly<ArgumentException>()
.WithParameterName("name");
}

[Fact]
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_not_implemented_at_all()
{
var act = () =>
{
using var _ = new AssertionScope();
typeof(int).Should().HaveExplicitProperty(typeof(IExplicitInterface), "Foo");
};

act.Should().Throw<XunitException>()
.WithMessage("Expected type System.Int32 to*implement *IExplicitInterface, but it does not.");
}
}

public class HaveExplicitPropertyOfT
Expand Down Expand Up @@ -395,6 +409,21 @@ public void When_asserting_a_type_does_not_have_an_explicit_property_with_an_emp
act.Should().ThrowExactly<ArgumentException>()
.WithParameterName("name");
}

[Fact]
public void Does_not_continue_assertion_on_explicit_interface_implementation_if_implemented()
{
var act = () =>
{
using var _ = new AssertionScope();
typeof(ClassExplicitlyImplementingInterface)
.Should().NotHaveExplicitProperty(typeof(IExplicitInterface), "ExplicitStringProperty");
};

act.Should().Throw<XunitException>()
.WithMessage("Expected *ClassExplicitlyImplementingInterface* to*implement " +
"*IExplicitInterface.ExplicitStringProperty, but it does.");
}
}

public class NotHaveExplicitPropertyOfT
Expand Down
2 changes: 2 additions & 0 deletions docs/_pages/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ sidebar:
* Improved the failure message for `NotBeOfType` when wrapped in an `AssertionScope` and the subject is null - [#2399](https://github.com/fluentassertions/fluentassertions/pull/2399)
* 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)
* 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)
* 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)
* 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)


### Breaking Changes (for users)
Expand Down