question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Type.Should().Be(Type) doesn't support open generics

See original GitHub issue

I would expect this:

var response = result.GetType(); // returns ICustomInterface<int>
response.Should().Be(typeof(ICustomInterface<>));

to succeed.

Instead it fails with message:

Expected type to be ICustomInterface1, but found ICustomInterface1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].

I believe this is related to the change made for: #458

P.S. Workaround for now:

result.GetType().GetGenericTypeDefinition()
        .Should().Be(typeof(ICustomInterface<>));

I am also using this in a few “And” constraints which forces me to do:

 result.Should().NotBeNull()
    .And.Subject.GetGenericTypeDefinition().Should().Be(typeof(ICustomInterface<>));

Which is ugly and stops me being able to add more assertions as the Subject is now the GenericTypeDefinition and not the actual Type I am validating.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
jnyrupcommented, Oct 30, 2018

Thanks for raising this issue. First of all to assert on the type of an object you should probably use one of

response.Should().BeAssignableTo<TType>()
response.Should().BeAssignableTo(typeof(TType))

to test that response is assignable to TType.

The changes related to #458 added support for open generic when using BeOfType().

Here are my findings:

First let’s see how Fluent assertions behaves for non-generic types to establish a baseline.

interface IInterface { }

class Base : IInterface { }

class Derived : Base { }

Derived derived = new Derived();
derived.Should().BeOfType<Derived>(); // success
derived.Should().BeAssignableTo<Base>(); // success
derived.Should().BeAssignableTo<IInterface>(); // success
derived.Should().BeOfType<Base>(); // fail
derived.Should().BeOfType<IInterface>(); // fail

Base baseClass = new Derived();
baseClass.Should().BeOfType<Derived>(); // success
baseClass.Should().BeAssignableTo<Base>(); // success
baseClass.Should().BeAssignableTo<IInterface>(); // success
baseClass.Should().BeOfType<Base>(); // fail
baseClass.Should().BeOfType<IInterface>(); // fail

IInterface interfaced = new Derived();
interfaced.Should().BeOfType<Derived>(); // success
interfaced.Should().BeAssignableTo<Base>(); // success
interfaced.Should().BeAssignableTo<IInterface>(); // success
interfaced.Should().BeOfType<Base>(); // fail
interfaced.Should().BeOfType<IInterface>(); // fail

And the results for doing the same assertions on generic types, both with open and closed generic types.

interface IInterface<T> { }

class Base<T> : IInterface<T> { }

class Derived<T> : Base<T> { }

Derived<int> derived2 = new Derived<int>();
derived2.Should().BeOfType<Derived<int>>(); // success
derived2.Should().BeAssignableTo<Base<int>>(); // success
derived2.Should().BeAssignableTo<IInterface<int>>(); // success
derived2.Should().BeOfType<Base<int>>(); // fail
derived2.Should().BeOfType<IInterface<int>>(); // fail

derived2.Should().BeOfType(typeof(Derived<>)); // success
derived2.Should().BeAssignableTo(typeof(Base<>)); // <-------------- now fails
derived2.Should().BeAssignableTo(typeof(IInterface<>)); // <-------- now fails
derived2.Should().BeOfType(typeof(Base<>)); // fail
derived2.Should().BeOfType(typeof(IInterface<>)); // fail

Base<int> baseClass2 = new Derived<int>();
baseClass2.Should().BeOfType<Derived<int>>(); // success
baseClass2.Should().BeAssignableTo<Base<int>>(); // success
baseClass2.Should().BeAssignableTo<IInterface<int>>(); // success
baseClass2.Should().BeOfType<Base<int>>(); // fail
baseClass2.Should().BeOfType<IInterface<int>>(); // fail

baseClass2.Should().BeOfType(typeof(Derived<>)); // success
baseClass2.Should().BeAssignableTo(typeof(Base<>)); // <-------------- now fails
baseClass2.Should().BeAssignableTo(typeof(IInterface<>)); // <-------- now fails
baseClass2.Should().BeOfType(typeof(Base<>)); // fail
baseClass2.Should().BeOfType(typeof(IInterface<>)); // fail

IInterface<int> interfaced2 = new Derived<int>();
interfaced2.Should().BeOfType<Derived<int>>(); // success
interfaced2.Should().BeAssignableTo<Base<int>>(); // success
interfaced2.Should().BeAssignableTo<IInterface<int>>(); // success
interfaced2.Should().BeOfType<Base<int>>(); // fail
interfaced2.Should().BeOfType<IInterface<int>>(); // fail

interfaced2.Should().BeOfType(typeof(Derived<>)); // success
interfaced2.Should().BeAssignableTo(typeof(Base<>)); // <-------------- now fails
interfaced2.Should().BeAssignableTo(typeof(IInterface<>)); // <-------- now fails
interfaced2.Should().BeOfType(typeof(Base<>)); // fail
interfaced2.Should().BeOfType(typeof(IInterface<>)); // fail

To summarize:

  • BeOfType(typeof(GenericType<>)) works as expected
  • BeAssignableTo(typeof(GenericType<>)) does not work as expected.

If agree that BeAssignableTo should have the same behavior as BeOfType when asserting on open generic types.

Whoever takes up this task, should assure consistency in the behavior between:

  • BeOfType(Type)
  • NotBeOfType(Type)
  • BeAssignableTo(Type)
  • NotBeAssignableTo(Type)

Currently only BeOfType(Type) has support for open generic types.

0reactions
jnyrupcommented, Nov 9, 2018

Support for open generic was contributed in #955 and released with Fluent Assertions 5.5.0.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What exactly is an "open generic type" in .NET? [duplicate]
The C# language defines an open type to be a type that's either a type argument or a generic type defined with unknown...
Read more >
Open and Closed Generic Types in C# | Pluralsight
In this guide, we will take a look at deeper at what generics are and what their open or closed properties mean.
Read more >
Partially closed generic types
An open generic type is simply a generic type whose type parameters have not been specified.
Read more >
Documentation - Generics
When you begin to use generics, you'll notice that when you create generic functions like identity , the compiler will enforce that you...
Read more >
How To Use Generics in Go
Using generic types allows you to interact directly with your types, leading to cleaner and easier-to-read code. In this tutorial, you will ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found