Xunit multiple IClassFixtures
Asked Answered
B

4

16

My question is How to setup multiple fixtures in one test class?

But the constructor of Zoo class can not handle multiple fixtures.

For exemple:

public class Zoo : IClassFixture<Tiger>, IClassFixture<Wolf>, IClassFixure<Bird>
{
   private IFixture fixture;
   public Zoo(IFixture fixture) 
   { 
    this.fixture = fixture; 
   }

   [Fact]
   public void TestAnimal()
   {
    //Arrange 
    int actualBonesCount = this.fixture.BonesCount;
    int expectedBonesCount = 2;

    //Act & Assert
    Assert.Equal(expectedBonesCount, actualBonesCount );
   }
} 

A tiger class

public class Tiger : FixtureBase
{
   public Tiger()
   {
    this.BonesCount  = 4;
   }
}

A bird class

public class Bird: FixtureBase
{
   public Bird()
   {
    this.BonesCount  = 2;
   }
}

Test fixture base class

public class FixtureBase : IFixture
{
   public int BonesCount { get; set; }
}

And interface

public interface IFixture
{
   int BonesCount { get; set; }
}
Brownfield answered 30/3, 2016 at 20:50 Comment(0)
A
16

First of all, let's recall when we want to use Class Fixtures in xUnit:

When to use: when you want to create a single test context and share it among all the tests in the class, and have it cleaned up after all the tests in the class have finished.

As you've said, you'd like to reuse methods from Zoo test class to implementation tests. Since implementations use inheritance, why not use inheritance for test classes too?

public abstract class Zoo
{
    protected readonly IFixture Fixture;

    protected Zoo(IFixture fixture)
    {
       Fixture = fixture;
    }

    [Fact]
    public void TestAnimal()
    {
        //Arrange 
        int actualBonesCount = Fixture.BonesCount;
        int expectedBonesCount = 2;

        //Act & Assert
        Assert.Equal(expectedBonesCount, actualBonesCount);
    }
}

public class BirdTests : Zoo, IClassFixture<Bird>
{
    public BirdTests(Bird fixture)
       : base(fixture)
    {
    }
}

public class TigerTests : Zoo, IClassFixture<Tiger>
{
    public BirdTests(Tiger fixture)
       : base(fixture)
    {
    }
}

Still, I don't really get how would you like for each test to pass as you've hardcoded BonesCount to 2 in a generic test.

Anthesis answered 30/3, 2016 at 21:20 Comment(4)
Tanks for your answer! This is not really that's test scenario I'm looking for. I want it to look like MemberData with many test data but still have some functionality as IClassFixture with many test methods.Brownfield
I'm not 100% sure if I understand your requirement. Any chance you can update your example with some explanation and possible scenario? Thanks!Anthesis
Hi again, for exemple we ha a test class with test methods inside. public class ZooTest { [Fact] public void TestEyesCount() { } [Fact] public void TestSkinQuality() { } } and we have 2 classes like Tiger, and Spider with some information about this animals. (Like Spider have 32 eyes and Tiger only 2) And my question is how to test Tiger and Bird using the same ZooTest class while number of eyes (and more) changed between this two test cases? And classes Tiger, Bird should initialize once and go by testsBrownfield
I think I understand what you mean. I've posted approach I use extensively through inheritance of my xUnit tests. You could also use [Theory] for each test but that would be much more messy and would be hard to implement with IClassFixture<T> as [Theory] scope is I guess test method it decorates. :)Anthesis
B
4

This is solution i came to after your comment. Thank you very much!

public static IEnumerable<object[]> TestCases = 
new TheoryData<Animal>{ new Bird { Eyes = 2 } };

[Theory]
[MemberData(nameof(TestCases))]
public void TestEyes(Animal email)
{
//Arrange & Act & Assert
}
Brownfield answered 31/3, 2016 at 16:40 Comment(0)
G
0

You can use Collection Fixtures to use shared-context between multiple test classes

When to use:

when you want to create a single test context and share it among tests in several test classes, and have it cleaned up after all the tests in the test classes have finished.

public class ServerFixture : IDisposable
{
    public ServerFixture()
    { 
    } 
    public void Dispose()
    { 
    } 
}

[CollectionDefinition("Server collection")]
public class ServerCollection : ICollectionFixture<ServerFixture>
{
}

[Collection("Server collection")]
public class ServerTestScenarios_1
{
    ServerFixture fixture;

    public ServerTestScenarios_1(ServerFixture fixture)
    {
        this.fixture = fixture;
    }
}

[Collection("Server collection")]
public class ServerTestScenarios_2
{
    ServerFixture fixture;
    
    public ServerTestScenarios_2(ServerFixture fixture)
    {
        this.fixture = fixture;
    }
}

you can see : https://xunit.net/docs/shared-context#collection-fixture

I hope that help

Guenzi answered 11/6, 2023 at 18:59 Comment(0)
G
0

Your class declaration is fine. You can inherit from multiple class fixtures. However, you need to add them all to the constructor and create separate properties for each one.

public class Zoo : IClassFixture<Tiger>, IClassFixture<Wolf>, IClassFixure<Bird>
{
   private Tiger tigerFixture;
   private Wolf wolfFixture;
   private Bird birdFixture;

   public Zoo(Tiger tiger, Wolf wolf, Bird bird) 
   { 
       tigerFixture = tiger;
       wolfFixture = wolf;
       birdFixture = bird;
   }

If you want to run the same test for each fixture, you'll have to make it a Theory and pass in some argument that indicates which fixture should be used for each run of the test.

Gook answered 17/1 at 17:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.