0

I have the following problem. I want to test a generic class MyClass<'T>. However, I don't really care about the type being used for T. However, when I create an instance of MyClass in the test, I need to specify what T is supposed to be. I can do it like that:

var sut = new MyClass<Object>();

It works of course, however, I don't like to put Object specifically, because it suggests to someone reading this test that maybe Object type is somehow significant here.

I like the approach used by AutoFixture where each value is created using fixture.Create<T>(), because it shows the reader that the value is not really important. Example of that:

[Fact]
public void IntroductoryTest()
{
    // Arrange
    Fixture fixture = new Fixture();

    int expectedNumber = fixture.Create<int>();
    MyClass sut = fixture.Create<MyClass>();
    // Act
    int result = sut.Echo(expectedNumber);
    // Assert
    Assert.Equal(expectedNumber, result);
}

In the code above we don't care about the number, so we use AutoFixture as an abstraction about the fact that some number is needed.

Is there a solution like that, but in regard to types being used with generic classes?

Something like: var sut = MyClass<fixture.GenericType>(); I don't care here what is the actual type. To explain my use-case a bit more: I have AutoFac module that is used to register MyClass. In my test I just want to test if the module registers MyClass properly. For that registration I don't want to specify some exact type to be used for MyClass. It can be any.

Loreno
  • 668
  • 8
  • 26
  • If your testing a class that's generic and you don't need that generic this is a strong indicator that your class needs further decomposition. Why do you have to specify a generic that you don't need! Refactor your class into 2 classes, one that needs the generic and one that doesn't – Liam Feb 07 '19 at 12:51
  • Could you provide an example how `fixture.Create()` is used? – Alexander Feb 07 '19 at 13:37
  • @Alexander I edited my question – Loreno Feb 07 '19 at 14:29
  • @Liam Not really possible, my class in reality is a configuration mapper. It needs T to specify the type of configuration. My test does not really test that class - it tests the AutoFac module used to register that class. – Loreno Feb 07 '19 at 14:30
  • Why don't you take something random like String/int? Or maybe you can make 2 or 3 tests with different types. – keuleJ Feb 07 '19 at 14:48
  • I think you are over-thinking this. Just create an interface inside your test type called something like IGenericType and use that. Or use `object`, it doesn't matter that much as long as you are testing the functionality. – Grax32 Feb 08 '19 at 02:32
  • @Grax I know that it dosn't really matter. It's not something that is necessary, I was just wondering if some more elegant solution already exists and SO is a great place to ask such questions I think. – Loreno Feb 08 '19 at 08:16
  • 1
    http://blog.ploeh.dk/2011/05/09/GenericunittestingwithxUnit.net – Mark Seemann Feb 19 '19 at 14:20
  • @MarkSeemann Thanks for your article. Your approach still requires me to choose some types that I want to use as T, but this is best approach I've seen anyway. By the way, I am a fan of your publications, thanks for your work! – Loreno Feb 20 '19 at 07:20

2 Answers2

1

Probably you can define a base type say IBase, where all your T types implements IBase (Class1<T> where T : IBase) and then you can say var sut = new MyClass<IBase>();

Rahul
  • 76,197
  • 13
  • 71
  • 125
  • It does not really change anything. Object or IBase - they are both concrete types that I would have to mention in my tests. I want more abstraction. I want to inform the test that I need some type, but I don't care what type will be used. This is exactly what AutoFixture does, but it relates to types instead of instances. – Loreno Feb 07 '19 at 14:32
1

Currently AutoFixture doesn't support non-closed generic types activation and it's unlikely it will change one day.

Firstly it's caused by the fact that the C# language itself supports non-closed generic types for the reflection purpose only. You cannot declare variable of MyClass<> type without closing the type over particular T. Hence, even if AutoFixture substitute type and activate an instance for you, it will be hard to work with the result - you'd either need to use a variable of object type or rely on covariance if your type supports that. None of those options looks usable.

Secondly, the task itself is very hard as generic type could have constrains. Sometimes constrains could get crazy and e.g. have cyclic dependencies: class MyClass<T1, T2> where T1 : T2 where T2 : IEnumerable<T1>. The AutoFixture.Idioms library already tries to solve that problem for the GuardClauseAssertion, but the success is average. This feature requires dynamic type generation (which BTW is not supported on all the platforms) and very advanced usage of the Reflection.Emit API, which makes it very hard to implement correctly.

Alex Povar
  • 674
  • 3
  • 15