【问题标题】:NUnit Conditional Teardown?NUnit 有条件的拆解?
【发布时间】:2023-03-24 16:55:01
【问题描述】:

有没有办法在 NUnit 中进行有条件的 TearDown?

我有一个 TestFixture,它只需要为几个测试运行清理代码,我真的不想这样做:

  1. 对每个测试运行 TearDown 方法
  2. 如果可以避免的话,创建一个私有帮助方法并从需要清理的测试中调用它

【问题讨论】:

    标签: unit-testing nunit


    【解决方案1】:

    遗憾的是没有。

    你不能在 [TestFixtureTearDown] 中进行清理,所以一旦所有测试都完成了吗?我想这取决于在下一次测试运行之前是否需要进行清理

    或者,将那些需要清理的测试放在另一个类/TextFixture 中,远离其他测试。然后你可以在那里使用不需要有条件的 TearDown。

    编辑: 我刚刚想到的一件事,可以用来实现目标,尽管对于这个特殊的需求可能实际上并不值得,那就是你可以扩展 NUnit - 创建你自己的自定义属性,你可以随心所欲地处理它。这是提到here。就像我说的那样,我认为你真的不应该为此走这条路,但了解一点很有用

    【讨论】:

    • 好建议,但我必须在特定测试后运行清理以准备下一次测试。没有考虑过不同的测试夹具。谢谢推荐。
    • 不用担心,前几天有一个非常相似的问题:stackoverflow.com/questions/1194198/… 另外,我刚刚想到了一个替代方案..请在我的回答中查看编辑。我不认为这是最好的解决方案/或者特别是您想花时间做的事情,但无论如何都会包括在内。
    • @Mark - 好的做法是永远不要让任何测试相互依赖
    • @AutomatedTester - 这就是我试图将其合并到 TearDown 方法中的原因 - 并且通过编写清理代码来停止重复自己。
    【解决方案2】:

    您可以在基类中拥有主要的 TearDown:

    [TearDown]
    public virtual void TearDown()
    {
      // Tear down things here
    }
    

    然后在您拥有不应运行拆卸代码的测试的类中覆盖它:

    [TearDown]
    public override void TearDown()
    {
      // By not calling base.TearDown() here you avoid tearing down
    }
    

    【讨论】:

      【解决方案3】:

      使用 BaseTest 中的测试扩展您的所有课程

          public class BaseTest
          {
              [SetUp]
              public void BeforeTest()
              {
                  GetService<NUnitHooksController>().ExecuteBeforeTestHooks(this);
              }
      
              [TearDown]
              public void AfterTest()
              {
                  GetService<NUnitHooksController>().ExecuteAfterTestHooks(this);
              }
          }
      

      使用 AfterTest 和 BeforeTest 挂钩。无论有无类别都适用。

          public class ExampleTest : BaseTest
          {
              [Test, Category("asdasd")]
              public void Test01()
              {
                 ...
              }
      
              [AfterTest("asdasd")]
              public void ExampleHook()
              {
                 ...
              }
      
          }
      
          public class NUnitHooksController
          {
              private readonly ILogger _log;
      
              public NUnitHooksController(ILogger log)
              {
                  _log = log;
              }
      
              public void ExecuteBeforeTestHooks(object testClass)
              {
                  ExecuteHooks(testClass, typeof(BeforeTestAttribute));
              }
      
              public void ExecuteAfterTestHooks(object testClass)
              {
                  ExecuteHooks(testClass, typeof(AfterTestAttribute));
              }
      
              private MethodInfo[] GetHookMethods(object currentTestClass, Type attributeType)
              {
                  return currentTestClass
                      .GetType()
                      .GetMethods()
                      .Where(m => m.GetCustomAttributes(attributeType, false).Length > 0)
                      .ToArray();
              }
      
              private void ExecuteHooks(object testClass, Type requiredAttributeType)
              {
                  var hooks = GetHookMethods(testClass, requiredAttributeType);
                  var testCategories = GetTestCategories();
      
                  foreach (var hook in hooks)
                  {
                      var allAttributes = hook.GetCustomAttributes(requiredAttributeType, true);
                      foreach (var attribute in allAttributes)
                      {
                          if (!attribute.GetType().IsEquivalentTo(requiredAttributeType))
                          {
                              continue;
                          }
      
                          var hookCategories = GetCategoriesFromAttribute(attribute);
      
                          // if we do not have specific category on hook
                          // or we have at least one same category on hook and test
                          if (!hookCategories.Any() || hookCategories.Intersect(testCategories).Any())
                          {
                              ExecuteHookMethod(testClass, hook);
                          }
                      }
                  }
              }
      
              private object[] GetTestCategories()
              {
                  return TestContext.CurrentContext.Test.Properties["Category"].ToArray();
              }
      
              private void ExecuteHookMethod(object testClass, MethodInfo method)
              {
                  var hookName = method.Name;
                  _log.Information($"Executing - '{hookName}' hook");
      
                  try
                  {
                      method.Invoke(testClass, Array.Empty<object>());
                  }
                  catch (Exception e)
                  {
                      _log.Error($"Executing of - '{hookName}' hook failed - {e}");
                  }
              }
      
              private string[] GetCategoriesFromAttribute(object attribute)
              {
                  if (attribute is BeforeTestAttribute beforeTestAttribute)
                  {
                      return beforeTestAttribute.Categories;
                  }
      
                  if (attribute is AfterTestAttribute afterTestAttribute)
                  {
                      return afterTestAttribute.Categories;
                  }
      
                  throw new ArgumentException($"{attribute.GetType().FullName} - does not have categories");
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多