C# 具有泛型类型的单元测试
我想用泛型测试单元测试,我正在努力找到正确的方法 我有这个C# 具有泛型类型的单元测试,c#,unit-testing,generics,testing,nunit,C#,Unit Testing,Generics,Testing,Nunit,我想用泛型测试单元测试,我正在努力找到正确的方法 我有这个 [TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)] public void ReadFromCsvFileWithConfigurationMapTest<T,Tmap>(T t, Tmap tmap, int totalRowsExptected) { //Arrange //Act var record
[TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)]
public void ReadFromCsvFileWithConfigurationMapTest<T,Tmap>(T t, Tmap tmap, int totalRowsExptected)
{
//Arrange
//Act
var records = csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
var result = new List<object>(records);
//Assert
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(totalRowsExptected);
}
[TestCase(typeof(CalendarGeneralCsv),typeof(CalendarGeneralCsvMap),121]
从CSVFileWithConfigurationMapTest读取的公共无效数据(T T、Tmap Tmap、int TOTALLOWSExtected)
{
//安排
//表演
var records=csvService.ReadFileCsv(_csvToRead,“,”)作为IEnumerable;
var结果=新列表(记录);
//断言
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(TotalRowSextected);
}
错误在这一行
var records = csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
var records=csvService.ReadFileCsv(_csvToRead,“,”)作为IEnumerable;
表示T和Tmap必须是引用类型。考虑使用反射来调用测试中的泛型主题
[TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)]
public void ReadFromCsvFileWithConfigurationMapTest(Type t, Type tmap, int totalRowsExptected) {
//Arrange
//...
var serviceType = csvService.GetType();
var method = serviceType.GetMethod("ReadFileCsv");
var genericMethod = method.MakeGenericMethod(t, tmap);
var parameters = new object[] { _csvToRead, "," };
//Act
var records = genericMethod.Invoke(csvService, parameters) as IEnumerable<object>;
//Above same as csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
//Assert
records.Should().NotBeNull();
var result = new List<object>(records);
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(totalRowsExptected);
}
以便访问其成员信息
按名称查找要调用的所需成员
var method = serviceType.GetMethod("ReadFileCsv");
并将提供的类型参数用于泛型参数
var genericMethod = method.MakeGenericMethod(t, tmap);
可以使用传递的参数在服务实例上调用泛型成员
var records = genericMethod.Invoke(csvService, new object[] { _csvToRead, "," }) as IEnumerable<object>;
var records=genericMethod.Invoke(csvService,新对象[]{{u csvToRead,,“})作为IEnumerable;
或者,如果您不打算在不同类型的测试中使用多个TestCase
属性,则无需为测试提供任何通用参数。可以将类型显式传递到类型参数中:
public void ReadFromCsvFileWithConfigurationMapTest()
{
//Arrange
//Act
var records = csvService.ReadFileCsv<CalendarGeneralCsv, CalendarGeneralCsvMap>(_csvToRead, ",") as IEnumerable<object>;
var result = new List<object>(records);
//Assert
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(121);
}
public void ReadFromCsvFileWithConfigurationMapTest()
{
//安排
//表演
var records=csvService.ReadFileCsv(_csvToRead,“,”)作为IEnumerable;
var结果=新列表(记录);
//断言
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(121);
}
但是,如果您确实希望为多个类型/值运行相同的测试用例,则可以将实际测试逻辑提取到通用方法中。然后,您可以为要测试的每一组数据创建一个新的测试,并将类型显式传递到泛型方法中:
[Test]
public void ReadFromCsvFileWithConfigurationMapTest() => ReadFromCsvFile<CalendarGeneralCsv, CalendarGeneralCsvMap>(121);
[Test]
public void ReadFromCsvFileWithOtherMapTest() => ReadFromCsvFile<CalendarGeneralCsv, OtherGeneralCsvMap>(151);
private void ReadFromCsvFile<T, TMap>(int expectedValue)
{
//Arrange
//Act
var records = csvService.ReadFileCsv<T, TMap>(_csvToRead, ",") as IEnumerable<object>;
var result = new List<object>(records);
//Assert
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(expectedValue);
}
[测试]
public void ReadFromCsvFileWithConfigurationMapTest()=>ReadFromCsvFile(121);
[测试]
public void ReadFromCsvFileWithOtherMapTest()=>ReadFromCsvFile(151);
私有void ReadFromCsvFile(int expectedValue)
{
//安排
//表演
var records=csvService.ReadFileCsv(_csvToRead,“,”)作为IEnumerable;
var结果=新列表(记录);
//断言
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(expectedValue);
}
虽然已经有了一个公认的答案(注意:自从我最初发布以来,答案已经改变了),但我想提供一种使用反射的替代方法。将试验方法分为两种方法,跳板法和通用试验方法。有几个优点:
- 通用测试方法看起来更像任何其他测试方法。它没有不相关的反射
- 通用测试方法可以正常地进行,也因为它没有混合不相关的反射
- 对被测组件的更改更有可能触发测试项目中的编译器错误,因此您知道需要更新通用测试方法,可能还有springboard方法。另外,由于抛出异常的位置,在运行时更清楚的是,这是因为支持反射,而不是组件的使用方式
- springboard方法不需要知道有关被测试组件的任何信息,只需要知道如何调用通用测试方法
- 因为变化很小,所以图案可以很容易且一致地复制
[TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)]
[TestCase(typeof(CalendarCustomCsv), typeof(CalendarCustomCsvMap), 80)]
public void ReadFromCsvFileWithConfigurationMapTest(Type t, Type tmap, int totalRowsExpected)
{
GetType().GetMethod(nameof(GenericReadFromCsvFileWithConfigurationMapTest))
.MakeGenericMethod(t, tmap) // <-- Type parameters go here
.Invoke(this, new object[] { totalRowsExpected }); // <-- inputs go here
}
public void GenericReadFromCsvFileWithConfigurationMapTest<T, Tmap>(int totalRowsExpected)
where T : class
where Tmap : class
{
// Arrange
// Act
var records = csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
// Assert
records.Should().NotBeNull();
var result = new List<object>(records);
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(totalRowsExptected);
}
[TestCase(typeof(CalendarGeneralCsv),typeof(CalendarGeneralCsvMap),121]
[TestCase(typeof(CalendarCustomCsv),typeof(CalendarCustomCsvMap),80)]
从CSVFileWithConfigurationMapTest读取的公共无效数据(t型、tmap型、int totalRowsExpected)
{
GetType().GetMethod(名称(GenericReadFromCsvFileWithConfigurationMapTest))
.MakeGenericMethod(t,tmap)//我通常不会回答已经有几个答案并且有一个被接受的问题,但它们似乎都基于这样的假设,即测试方法不可能是通用的。它们肯定可以。我的记忆告诉我,这一点曾经被很好地记录下来,但似乎不再存在了——或者我的记忆是错误的——这就解释了为什么你可能认为这是不可能的
可能通用解决方案在这里可能不是最好的,但这似乎是一件有趣的事情,可以尝试更好的解决方案,也可以澄清为什么接受的解决方案更好。我只能用已经提供的信息来说明这一点,但如果jolynice愿意合作,也许我们可以学到一些东西。:-)
所以…这是一个解决方案的初步尝试,如果有更多信息回来,我会编辑它
问题中的原始解决方案会导致错误,因为泛型方法ReadFileCsv(…)中的约束
未满足。我们不知道它们是什么,但从错误中可以看出,它们包括t:class
和Tmap:class
。因此,获得正确答案的第一步是重现对测试方法本身调用的方法的所有约束
更新:这段代码实际上不起作用。简而言之,我在本地拥有这项功能,我认为它已经添加到NUnit中,但它没有。请参阅下面的更新文本
[TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)]
public void ReadFromCsvFileWithConfigurationMapTest<T,Tmap>(int totalRowsExptected)
where T : class
where Tmap : class
{
//Arrange
//Act
var records = csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
var result = new List<object>(records);
//Assert
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(totalRowsExptected);
}
[TestCase(typeof(CalendarGeneralCsv),typeof(CalendarGeneralCsvMap),121]
从CSVFileWithConfigurationMapTest读取的公共void(int TotalRowSextected)
T:在哪里上课
Tmap:class在哪里
{
//安排
//表演
var records=csvService.ReadFileCsv(_csvToRead,“,”)作为IEnumerable;
var结果=新列表(记录);
//断言
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(TotalRowSextected);
}
更新对@Jannes评论的回应
[TestCase(typeof(CalendarGeneralCsv), typeof(CalendarGeneralCsvMap), 121)]
public void ReadFromCsvFileWithConfigurationMapTest<T,Tmap>(int totalRowsExptected)
where T : class
where Tmap : class
{
//Arrange
//Act
var records = csvService.ReadFileCsv<T, Tmap>(_csvToRead, ",") as IEnumerable<object>;
var result = new List<object>(records);
//Assert
result.Should().NotBeNullOrEmpty();
result.Should().HaveCount(totalRowsExptected);
}