如何在NUnit中使用不同对象作为测试用例以消除代码冗余?
问题:使用NUnit TestCase特性合并重复安装测试方法是否可行?
我正尝试为不同产品(以类形式存在)及不同时间版本编写更新测试。当前有两个重复的安装测试方法,分别针对去年和前年发布的产品,执行安装并验证是否成功,这是更新测试的前置步骤。为消除代码冗余,我尝试使用TestCase特性将其合并为一个测试方法,但未能成功实现,请问这种方式是否可行?
相关代码如下:
测试类定义:
public class UpdateTest { private static Product ProductLastYear; private static Product ProductTwoYearsAgo; public UpdateTest(Product product) { var previousReleasedProducts = new Products(); ProductLastYear = previousReleasedProducts.GetProduct(Years.GetLastYear()); ProductTwoYearsAgo = previousReleasedProducts.GetProduct(Years.GetTwoYearsAgo()); } }
原重复测试代码:
[Test, Category("UpdateLastYear"), Order((int) NunitTestOrderEnum.Order.First)] public void InstallPreviousReleasedProduct() { using (var controller = new BootstrapperController(ProductLastYear)) { controller.Install(); } var successfulInstallation = InstallationValidator.ValidateInstall(ProductLastYear); Assert.That(successfulInstallation, Is.True); } [Test, Category("UpdateTwoYearsAgo"), Order((int) NunitTestOrderEnum.Order.First)] public void InstallTwoYearsAgoProduct() { using (var controller = new BootstrapperController(ProductTwoYearsAgo)) { controller.Install(); } var successfulInstallation = InstallationValidator.ValidateInstall(ProductTwoYearsAgo); Assert.That(successfulInstallation, Is.True); }
尝试的合并代码(未成功):
[TestCase(ProductLastYear), Category("UpdateLastYear"), Order((int) NunitTestOrderEnum.Order.First)] [TestCase(ProductTwoYearsAgo), Category("UpdateTwoYearsAgo"), Order((int) NunitTestOrderEnum.Order.First)] public void InstallPreProduct(Product product) { using (var controller = new BootstrapperController(product)) { controller.Install(); } var successfulInstallation = InstallationValidator.ValidateInstall(product); Assert.That(successfulInstallation, Is.True); }
解答
直接使用[TestCase]传入Product类实例的方式不可行,原因是NUnit要求[TestCase]的参数必须是编译时常量(如字符串、数值、枚举值),而ProductLastYear和ProductTwoYearsAgo是运行时初始化的静态对象,无法作为[TestCase]的参数使用,编译或运行时会报错。
要实现合并测试方法的目标,推荐使用NUnit的[TestCaseSource]特性,它支持返回运行时生成的测试数据,同时可以为每个测试用例单独配置分类、顺序等属性。
修正方案一:基础版TestCaseSource
- 先调整测试类的初始化逻辑,使用NUnit的
[OneTimeSetUp]替代构造函数做一次性初始化(更符合NUnit的测试生命周期规范):
public class UpdateTest { private static Products _previousReleasedProducts; [OneTimeSetUp] public void Setup() { _previousReleasedProducts = new Products(); } }
- 定义测试数据源方法,返回需要测试的
Product实例:
public static IEnumerable<Product> PreviousProductTestCases() { yield return _previousReleasedProducts.GetProduct(Years.GetLastYear()); yield return _previousReleasedProducts.GetProduct(Years.GetTwoYearsAgo()); }
- 改造测试方法,关联数据源:
[Test, TestCaseSource(nameof(PreviousProductTestCases)), Order((int)NunitTestOrderEnum.Order.First)] public void InstallPreProduct(Product product) { using (var controller = new BootstrapperController(product)) { controller.Install(); } var successfulInstallation = InstallationValidator.ValidateInstall(product); Assert.That(successfulInstallation, Is.True); }
修正方案二:带自定义分类的TestCaseSource
如果需要为每个测试用例单独设置Category,可以返回TestCaseData对象来配置更多属性:
public static IEnumerable PreviousProductTestCasesWithCategories() { var lastYearProduct = _previousReleasedProducts.GetProduct(Years.GetLastYear()); yield return new TestCaseData(lastYearProduct) .SetCategory("UpdateLastYear"); var twoYearsAgoProduct = _previousReleasedProducts.GetProduct(Years.GetTwoYearsAgo()); yield return new TestCaseData(twoYearsAgoProduct) .SetCategory("UpdateTwoYearsAgo"); }
对应的测试方法:
[Test, TestCaseSource(nameof(PreviousProductTestCasesWithCategories)), Order((int)NunitTestOrderEnum.Order.First)] public void InstallPreProduct(Product product) { // 测试逻辑不变 }
内容的提问来源于stack exchange,提问作者kklaw
相关产品推荐
相关产品推荐

