NUnit - очистка после отказа при испытании

Мы реализовали систему графического представления документов, которая хранит все, что это - изображения в полях блоба SQL2005. Существует несколько сотен ГБ в данный момент, и мы видим превосходное время отклика и минимальное снижение производительности. Кроме того, соответствие установленным требованиям франка, у нас есть слой промежуточного программного обеспечения, который архивирует недавно отправленные документы оптической системе устройства автоматической смены дисков, которая представляет их как стандартную файловую систему NTFS.

Мы были очень довольны результатами, особенно относительно:

  1. Простота Репликации и Резервного копирования
  2. Способность легко реализовать систему управления версиями документа
41
задан bh213 15 July 2009 в 14:44
поделиться

9 ответов

Эта идея меня заинтересовала, поэтому я немного покопался. NUnit не имеет этой возможности из коробки, но есть целая структура расширяемости, поставляемая с NUnit. Я нашел эту замечательную статью о расширении NUnit - это была хорошая отправная точка. Поигравшись с ним, я пришел к следующему решению: метод, украшенный настраиваемым атрибутом CleanupOnError , будет вызываться, если один из тестов в приспособлении не удался.

Вот как выглядит тест. :

  [TestFixture]
  public class NUnitAddinTest
  {
    [CleanupOnError]
    public static void CleanupOnError()
    {
      Console.WriteLine("There was an error, cleaning up...");
      // perform cleanup logic
    }

    [Test]
    public void Test1_this_test_passes()
    {
      Console.WriteLine("Hello from Test1");
    }

    [Test]
    public void Test2_this_test_fails()
    {
      throw new Exception("Test2 failed");
    }

    [Test]
    public void Test3_this_test_passes()
    {
      Console.WriteLine("Hello from Test3");
    }
  }

где атрибут просто:

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
  public sealed class CleanupOnErrorAttribute : Attribute
  {
  }

А вот как он выполняется из надстройки:

public void RunFinished(TestResult result)
{
  if (result.IsFailure)
  {
    if (_CurrentFixture != null)
    {
      MethodInfo[] methods = Reflect.GetMethodsWithAttribute(_CurrentFixture.FixtureType,
                                                             CleanupAttributeFullName, false);
      if (methods == null || methods.Length == 0)
      {
        return;
      }

      Reflect.InvokeMethod(methods[0], _CurrentFixture);
    }
  }
}

Но вот сложная часть: надстройку нужно поместить в каталог addins рядом с Бегун NUnit. Мой был помещен рядом с бегуном NUnit в каталоге TestDriven.NET:

C: \ Program Files \ TestDriven.NET 2.0 \ NUnit \ addins

(я создал каталог addins , его там не было)

EDIT Другое дело, что метод очистки должен be static !

Я собрал простое дополнение, вы можете загрузить исходный код из моего SkyDrive . Вам нужно будет добавить ссылки на nunit.framework.dll , nunit.core.dll и nunit.core.interfaces.dll в соответствующие места.

Несколько примечаний: класс атрибута можно разместить в любом месте вашего кода. Я не хотел помещать его в ту же сборку, что и сам надстройка, потому что он ссылается на две сборки Core NUnit, поэтому я поместил его в другую сборку. Только не забудьте изменить строку в CleanAddin.cs ,

20
ответ дан 27 November 2019 в 00:25
поделиться

Хотя можно было бы принудить nUnit сделать это, это не самая лучшая разумный дизайн, вы всегда можете где-то установить временный файл, и если этот файл существует, запустить очистку.

Я бы рекомендовал изменить ваш код, чтобы у вас были включены транзакции базы данных, а в конце теста просто верните базу данных в исходное состояние (например, отменить транзакцию, которая представляет ваши модульные тесты).

2
ответ дан 27 November 2019 в 00:25
поделиться

Да, есть. Вы можете использовать Teardown атрибут , который будет отключаться после каждого теста. Вы хотели бы применить тот сценарий «сброса» базы данных, который у вас есть, разорвать и повторно настроить до и после каждого теста.

Этот атрибут используется внутри TestFixture для предоставления общего набора функции, которые выполняются после выполняется каждый тестовый метод.

Обновление : на основе комментариев и обновления вопроса я бы сказал, что вы можете использовать атрибут teardown и частные переменные, чтобы указать, должно ли срабатывать содержимое метода.

Тем не менее, я также заметил, что вам не нужна сложная логика или код обработки ошибок.

Учитывая это, я бы подумал, что вам лучше всего подойдет стандартный Setup / Teardown . Не имеет значения, есть ли ошибка, и вам не нужен код обработки ошибок.

Если вам нужна специальная очистка, потому что следующие тесты зависят от успешного завершения текущего, я бы посоветовал еще раз посетить ваши тесты - они, вероятно, не должны зависеть друг от друга.

2
ответ дан 27 November 2019 в 00:25
поделиться

А как насчет использования блока Try-Catch, повторно генерирующего перехваченное исключение?

try
{
//Some assertion
}
catch
{
     CleanUpMethod();
     throw;
}
1
ответ дан 27 November 2019 в 00:25
поделиться

Другой вариант - иметь специальную функцию, которая будет генерировать ваши исключения, которая устанавливает переключатель в testfixture, который сообщает об исключении.

public abstract class CleanOnErrorFixture
{
     protected bool threwException = false;

     protected void ThrowException(Exception someException)
     {
         threwException = true;
         throw someException;
     }

     protected bool HasTestFailed()
     {
          if(threwException)
          {
               threwException = false; //So that this is reset after each teardown
               return true;
          }
          return false;
     }
}

Затем, используя ваш пример:

[TestFixture]
public class SomeFixture : CleanOnErrorFixture
{
    [Test]
    public void MyFailTest()
    {
        ThrowException(new InvalidOperationException());
    }

    [Test]
    public void MySuccessTest()
    {
        Assert.That(true, Is.True);
    }

    [TearDown]
    public void CleanUpOnError()
    {
        if (HasLastTestFailed()) CleanUpDatabase();
    }
}

Единственная проблема здесь в том, что трассировка стека приведет к CleanOnErrorFixture

1
ответ дан 27 November 2019 в 00:25
поделиться

Я бы хотел, чтобы phsr предлагал на данный момент, и когда вы можете себе это позволить, проведите рефакторинг тестов, чтобы им никогда не приходилось полагаться на те же данные, которые нужны другому тесту, или даже лучше абстрагировать уровень доступа к данным и имитировать результаты, полученные из этой базы данных .

1
ответ дан 27 November 2019 в 00:25
поделиться

Как это не удается? Можно ли поместить его в блок try (do test) / catch (fix broken db) / finally?

Или вы можете вызвать закрытый метод, чтобы исправить это, когда вы проверили состояние ошибки.

0
ответ дан 27 November 2019 в 00:25
поделиться

Один из вариантов, не упомянутых до сих пор, - заключить тест в объект TransactionScope, так что не имеет значения, что произойдет, поскольку тест никогда ничего не фиксирует в БД. 1133810] некоторые подробности о технике . Вероятно, вы сможете найти больше, если выполните поиск по модульному тестированию и области транзакций (хотя вы действительно выполняете интеграционное тестирование, если вы попадаете в БД). Я успешно использовал его в прошлом.

Этот подход прост, не требует очистки и обеспечивает изолированность тестов.

Править. Я только что заметил, что ответ Рэя Хейса также похож на мой.

1
ответ дан 27 November 2019 в 00:25
поделиться

Я не говорю, что это отличная идея, но она должна работать. Помните, что ошибки утверждения - это просто исключения. Также не забывайте, что есть также атрибут [TestFixtureTearDown], который запускается только один раз после того, как все тесты в приспособлении были выполнены.

Используя эти два факта, вы можете написать что-то вроде установки флага, если тесты завершились неудачно, и проверки значения флаг в тестовом приспособлении снят.

Я не рекомендую это, но это сработает. На самом деле вы не используете NUnit по назначению, но можете это сделать.


[TestFixture]
public class Tests {
     private bool testsFailed = false;

     [Test]
     public void ATest() {
         try {
             DoSomething();
             Assert.AreEqual(....);
         } catch {
            testFailed = true;
         }
     }

     [TestFixtureTearDown]
     public void CleanUp() {
          if (testsFailed) {
              DoCleanup();
          }
     }
}
0
ответ дан 27 November 2019 в 00:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: