Firstly, remove the campaign
from the test method and make it a parameter of the method. Then remove the Fact
attribute and add the Theory
attribute. The Fact
attribute is usually used to test where a collection of data is not involved. Tests that need to run multiple times for different data is usually decorated with Theory
:
[Theory]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass(Campaign campaign)
{
using (var context = new CampaignDbContext(_options))
{
_campaignService = new CampaignService(context);
var actualCampaign = await _campaignService.AddCampaignAsync(campaign);
Assert.Equal(campaign.Title, actualCampaign.Title);
Assert.Equal(campaign.StartDate, actualCampaign.StartDate);
Assert.Equal(campaign.EndDate, actualCampaign.EndDate);
}
}
There are several options to provide test data to a test method.
1. Inheirt from TheoryData<T>
TheoryData
is used to provide data for the parameters of test methods. It defines several generic overloads for varying method parameter lengths. AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
has one parameter Campaign
, so inherit from TheoryData<Campaign>
:
public class CampaignData : TheoryData<Campaign>
{
public CampaignData()
{
Add(new Campaign { Title = "Test1", StartDate = new DateTime(2021, 1, 5), EndDate = new DateTime(2021, 10, 6) });
Add(new Campaign { Title = "Test2", StartDate = new DateTime(2021, 2, 5), EndDate = new DateTime(2021, 9, 6) });
Add(new Campaign { Title = "Test3", StartDate = new DateTime(2021, 3, 5), EndDate = new DateTime(2021, 8, 6) });
Add(new Campaign { Title = "Test4", StartDate = new DateTime(2021, 4, 5), EndDate = new DateTime(2021, 7, 6) });
}
}
In the constructor, call the Add
method to add Campaign
objects to the collection. Now what you can do is use the ClassData
attribute:
[Theory]
[ClassData(typeof(CampaignData))]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass(Campaign campaign)
{
using (var context = new CampaignDbContext(_options))
{
_campaignService = new CampaignService(context);
var actualCampaign = await _campaignService.AddCampaignAsync(campaign);
Assert.Equal(campaign.Title, actualCampaign.Title);
Assert.Equal(campaign.StartDate, actualCampaign.StartDate);
Assert.Equal(campaign.EndDate, actualCampaign.EndDate);
}
}
Using ClassData(typeof(CampaignData))
, you are telling xUnit that CampaignData
class has all the parameters that this method needs to be called with.
2. Use MemberData
If you don't want to create a new class, you can also define the collection items as a static member of the same class. In the same class you have the AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
method, define a static property or field like this. The type of this property is an IEnumerable<object[]>
. It is a collection of object[]
arrays. Each object
array in the collection defines one parameter list to the test method, Your test method has one parameter Campaign campaign
. So each object array can contain a Campaign
object. Then on the AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
add the MemberData
attribute with the name of the property:
class YourTestClass
{
public static IEnumerable<object[]> Campaigns => new List<object[]>
{
new object[]
{
new Campaign { Title = "Test1", StartDate = new DateTime(2021, 1, 5), EndDate = new DateTime(2021, 10, 6) },
new Campaign { Title = "Test2", StartDate = new DateTime(2021, 2, 5), EndDate = new DateTime(2021, 9, 6) },
new Campaign { Title = "Test3", StartDate = new DateTime(2021, 3, 5), EndDate = new DateTime(2021, 8, 6) },
new Campaign { Title = "Test4", StartDate = new DateTime(2021, 4, 5), EndDate = new DateTime(2021, 7, 6) }
}
};
[Theory]
[MemberData(nameof(Campaigns))]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass(Campaign campaign)
{
using (var context = new CampaignDbContext(_options))
{
_campaignService = new CampaignService(context);
var actualCampaign = await _campaignService.AddCampaignAsync(campaign);
Assert.Equal(campaign.Title, actualCampaign.Title);
Assert.Equal(campaign.StartDate, actualCampaign.StartDate);
Assert.Equal(campaign.EndDate, actualCampaign.EndDate);
}
}
}
This will call the method AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
with each of those Campaign
objects in the object
arrays of Campaigns
property.
If you only have a couple of Campaign
objects to test, Sergei's method of using the InlineData
attribute is a good choice.
Reference: https://andrewlock.net/creating-strongly-typed-xunit-theory-test-data-with-theorydata/ (You can find more ways of doing this here)