如何添加更多测试数据来断言哪个 returns 对象?
How do I add more test data to assert which returns object?
我在 Xunit 中进行了这个集成测试。
[Fact]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass()
{
var campaign = new Campaign
{
Title = "Test",
StartDate = new DateTime(2021, 6, 5),
EndDate = new DateTime(2021, 6, 6)
};
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);
}
}
AddCampaignAsync
的代码如下:
public async Task<Campaign> AddCampaignAsync(Campaign campaign)
{
if (campaign.EndDate != null) {
if (campaign.StartDate > campaign.EndDate)
{
throw new Exception("The campaign start date cannot be greater than end date");
}
}
await _context.Campaigns.AddAsync(campaign);
await _context.SaveChangesAsync();
return campaign;
}
它工作正常。但是我可以传入 campaign
的 collection 作为测试数据,而不是只将一个 campaign
对象作为测试数据吗?
您可以使用数据驱动测试。
例如:
[Theory]
[InlineData("Test1", "2021-06-05", "2021-06-06")]
[InlineData("Test2", "2021-06-07", "2021-06-08")]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass(string title, DateTime startDate, DateTime endDate)
{
var campaign = new Campaign
{
Title = title,
StartDate = startDate,
EndDate = endDate
};
...
您可以在此处找到更多示例:https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/
首先,从测试方法中删除campaign
,并将其作为方法的参数。然后删除 Fact
属性并添加 Theory
属性。 Fact
属性通常用于测试不涉及数据集合的地方。需要对不同数据多次 运行 的测试通常用 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);
}
}
有几个选项可以为测试方法提供测试数据。
1。继承自 TheoryData<T>
TheoryData
用于为测试方法的参数提供数据。它为不同的方法参数长度定义了几个通用重载。 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
有一个参数 Campaign
,所以继承自 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) });
}
}
在构造函数中,调用Add
方法将Campaign
个对象添加到集合中。现在你可以做的是使用 ClassData
属性:
[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);
}
}
使用 ClassData(typeof(CampaignData))
,您告诉 xUnit CampaignData
class 具有调用此方法所需的所有参数。
2。使用 MemberData
如果您不想创建新的 class,您也可以将集合项定义为同一 class 的静态成员。在同一个 class 中,您有 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
方法,定义一个静态 属性 或像这样的字段。 属性 的类型是 IEnumerable<object[]>
。它是 object[]
个数组的集合。集合中的每个 object
数组都为测试方法定义了一个参数列表,您的测试方法有一个参数 Campaign campaign
。所以每个对象数组可以包含一个 Campaign
对象。然后在 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
添加 MemberData
属性,名称为 属性:
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);
}
}
}
这将使用 Campaigns
属性.
的 object
数组中的每个 Campaign
对象调用方法 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
如果您只有几个 Campaign
对象要测试,Sergei 使用 InlineData
属性的方法是一个不错的选择。
参考:https://andrewlock.net/creating-strongly-typed-xunit-theory-test-data-with-theorydata/(你可以在这里找到更多的方法)
我在 Xunit 中进行了这个集成测试。
[Fact]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass()
{
var campaign = new Campaign
{
Title = "Test",
StartDate = new DateTime(2021, 6, 5),
EndDate = new DateTime(2021, 6, 6)
};
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);
}
}
AddCampaignAsync
的代码如下:
public async Task<Campaign> AddCampaignAsync(Campaign campaign)
{
if (campaign.EndDate != null) {
if (campaign.StartDate > campaign.EndDate)
{
throw new Exception("The campaign start date cannot be greater than end date");
}
}
await _context.Campaigns.AddAsync(campaign);
await _context.SaveChangesAsync();
return campaign;
}
它工作正常。但是我可以传入 campaign
的 collection 作为测试数据,而不是只将一个 campaign
对象作为测试数据吗?
您可以使用数据驱动测试。
例如:
[Theory]
[InlineData("Test1", "2021-06-05", "2021-06-06")]
[InlineData("Test2", "2021-06-07", "2021-06-08")]
public async Task AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass(string title, DateTime startDate, DateTime endDate)
{
var campaign = new Campaign
{
Title = title,
StartDate = startDate,
EndDate = endDate
};
...
您可以在此处找到更多示例:https://andrewlock.net/creating-parameterised-tests-in-xunit-with-inlinedata-classdata-and-memberdata/
首先,从测试方法中删除campaign
,并将其作为方法的参数。然后删除 Fact
属性并添加 Theory
属性。 Fact
属性通常用于测试不涉及数据集合的地方。需要对不同数据多次 运行 的测试通常用 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);
}
}
有几个选项可以为测试方法提供测试数据。
1。继承自 TheoryData<T>
TheoryData
用于为测试方法的参数提供数据。它为不同的方法参数长度定义了几个通用重载。 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
有一个参数 Campaign
,所以继承自 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) });
}
}
在构造函数中,调用Add
方法将Campaign
个对象添加到集合中。现在你可以做的是使用 ClassData
属性:
[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);
}
}
使用 ClassData(typeof(CampaignData))
,您告诉 xUnit CampaignData
class 具有调用此方法所需的所有参数。
2。使用 MemberData
如果您不想创建新的 class,您也可以将集合项定义为同一 class 的静态成员。在同一个 class 中,您有 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
方法,定义一个静态 属性 或像这样的字段。 属性 的类型是 IEnumerable<object[]>
。它是 object[]
个数组的集合。集合中的每个 object
数组都为测试方法定义了一个参数列表,您的测试方法有一个参数 Campaign campaign
。所以每个对象数组可以包含一个 Campaign
对象。然后在 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
添加 MemberData
属性,名称为 属性:
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);
}
}
}
这将使用 Campaigns
属性.
object
数组中的每个 Campaign
对象调用方法 AddCampaignAsync_GivenUniqueTitle_GivenGoodDates_ShouldPass
如果您只有几个 Campaign
对象要测试,Sergei 使用 InlineData
属性的方法是一个不错的选择。
参考:https://andrewlock.net/creating-strongly-typed-xunit-theory-test-data-with-theorydata/(你可以在这里找到更多的方法)