Подтвердить что ты не робот

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

У нас есть несколько тестов NUnit, которые обращаются к базе данных. Когда один из них терпит неудачу, он может оставить базу данных в несогласованном состоянии - что не является проблемой, поскольку мы перестраиваем базу данных для каждого тестового прогона - но это может привести к сбою других тестов в одном и том же прогоне.

Можно ли обнаружить, что один из тестов не выполнен и выполняет какую-то очистку?

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

Обновить. Чтобы уточнить, я хотел бы, чтобы тесты были простыми и НЕ включали логику обработки ошибок или ошибок. Я также не хочу выполнять базу данных reset при каждом тестовом прогоне - только в случае сбоя теста. И этот код, вероятно, должен быть выполнен в методе Teardown, но я не знаю, каким образом получить информацию, если тест, который мы в настоящее время срываем с неудачной или успешной.

Update2

        [Test]
        public void MyFailTest()
        {
            throw new InvalidOperationException();
        }

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

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

Я ищу реализацию HasLastTestFailed()

4b9b3361

Ответ 1

Эта идея заинтересовала меня, поэтому я немного погубил. 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
  {
  }

И вот как он выполнен из addin:

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);
    }
  }
}

Но здесь сложная часть: addin должен быть помещен в каталог addins рядом с бегуном NUnit. Шахта была помещена рядом с бегуном NUnit в каталоге TestDriven.NET:

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

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

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

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

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

Надеюсь, что это поможет.

Ответ 2

Начиная с версии 2.5.7, NUnit позволяет Teardown обнаруживать, завершился ли последний тест. Новый класс TestContext позволяет тестировать доступ к информации о себе, включая TestStauts.

Подробнее см. http://nunit.org/?p=releaseNotes&r=2.5.7

[TearDown]
public void TearDown()
{
    if (TestContext.CurrentContext.Result.Status == TestStatus.Failed)
    {
        PerformCleanUpFromTest();
    }
}

Ответ 3

Да, есть. Вы можете использовать атрибут Teardown, который будет срываться после каждого теста. Вы хотите применить эту базу данных "reset" script, которую у вас есть, и отменить и переустановить до и после каждого теста.

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

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

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

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

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

Ответ 4

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

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

Ответ 5

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

try
{
//Some assertion
}
catch
{
     CleanUpMethod();
     throw;
}

Ответ 6

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

Вы также сможете лучше проверить свой ExceptionHandling.

Ответ 7

Другой вариант - иметь специальную функцию, которая будет генерировать ваши исключения, которая устанавливает переключатель в 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

Ответ 8

Один из вариантов, о которых не упоминалось до сих пор, заключается в том, чтобы завершить тестирование в объекте TransactionScope, поэтому не имеет значения, что происходит, когда тест никогда ничего не делает для БД.

Здесь некоторые сведения о технике. Вероятно, вы можете найти больше, если выполните поиск по модульному тестированию и транзакциям (хотя вы действительно выполняете интеграционное тестирование, если попадете в БД). Я использовал его в прошлом.

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

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

Ответ 9

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

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

Ответ 10

Я не говорю, что это отличная идея, но она должна работать. Помните, что ошибки утверждения являются лишь исключениями. Также не забывайте, что есть и атрибут [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();
          }
     }
}

Ответ 11

Вы можете добавить метод [TearDown] с помощью if (TestContext.CurrentContext.Result.Status != TestStatus.Passed)
некоторый код, который должен быть выполнен, если тест не выполнен.