Как обеспечить, чтобы очистка базы данных всегда выполнялась после теста? - PullRequest
17 голосов
/ 25 марта 2012

Рассмотрим следующий пример модульного теста.Комментарии в значительной степени объясняют мою проблему.

[TestMethod]
public void MyTestMethod()
{

  //generate some objects in the database
  ...

  //make an assert that fails sometimes (for example purposes, this fails always)
  Assert.IsTrue(false);

  //TODO: how do we clean up the data generated in the database now that the test has ended here?

}

Ответы [ 9 ]

26 голосов
/ 25 марта 2012

Есть два способа сделать это. Один использует атрибуты TestInitialize и TestCleanup для методов в классе теста.Они всегда будут выполняться до и после теста, соответственно.

Еще один способ - использовать тот факт, что сбои теста передаются организатору теста через исключения.Это означает, что блок try {} finally {} в вашем тесте можно использовать для очистки чего угодно после сбоя assert.

[TestMethod]
public void FooTest()
{
  try
  {
     // setup some database objects
     Foo foo = new Foo();
     Bar bar = new Bar(foo);
     Assert.Fail();
  }
  finally
  {
     // remove database objects.
  }
}

Очистка try / finally может стать очень грязной, если объектов многоочистить.Моя команда склоняется к вспомогательному классу, который реализует IDisposable.Он отслеживает, какие объекты были созданы, и помещает их в стек.Когда вызывается Dispose, элементы извлекаются из стека и удаляются из базы данных.

[TestMethod]
public void FooTest()
{
  using (FooBarDatabaseContext context = new FooBarDatabaseContext())
  {
    // setup some db objects.
    Foo foo = context.NewFoo();
    Bar bar = context.NewBar(foo);
    Assert.Fail();
  } // calls dispose. deletes bar, then foo.
}

Это дает дополнительное преимущество, заключающееся в переносе конструкторов в вызовах методов.Если сигнатуры конструктора меняются, мы можем легко изменить тестовый код.

9 голосов
/ 25 марта 2012

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

Для целей модульного тестирования, чтобы дать вам возможность протестировать только то, что вы хотите протестировать,вам нужно будет проектировать для тестирования .Обычно это включает в себя дополнительное использование интерфейсов (я предполагаю, что .NET из кода, который вы показали) и некоторую форму внедрения зависимостей (но не требует контейнера IoC / DI, если вы этого не хотите).Он также извлекает выгоду и поощряет вас создавать очень сплоченные (одноцелевые) и разобщенные (мягкие зависимости) классы в вашей системе.

Поэтому, когда вы тестируете бизнес-логику, которая зависит от данных из базы данных, вы должныобычно используют что-то вроде Repository Pattern и вводят fake / stub / mock IXXXRepository для модульного тестирования.Когда вы тестируете конкретный репозиторий, вам нужно либо выполнить тот тип очистки базы данных, о котором вы спрашиваете, либо вам нужно подкрутить / заглушить базовый вызов базы данных.Это действительно ваше дело.

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

В MS-Test вы можете использовать следующие атрибуты для объявления установки / разрыва: ClassInitialize , ClassCleanUp , TestInitialize , TestCleanUp .Другие фреймворки имеют конструкции с аналогичными именами.

Существует ряд фреймворков, которые могут помочь вам с насмешками / заглушками: Moq , Rhino Mocks , NMock, TypeMock , Кроты и заглушки (VS2010), VS11 Подделки (VS11 Beta) и т. Д. Если вы ищете платформы внедрения зависимостей, посмотритена такие вещи, как Ninject , Unity , Castle Windsor и т. д.

5 голосов
/ 25 марта 2012

Пара ответов:

  1. Если он использует реальную базу данных, я бы сказал, что это не «модульный тест» в самом строгом смысле этого слова. Это интеграционный тест. У юнит-теста таких побочных эффектов не должно быть. Подумайте об использовании библиотеки-макета для моделирования реальной базы данных. Rhino Mocks - это одно, но есть множество других.

  2. Если, однако, вся точка этого теста на самом деле взаимодействует с базой данных, то вы захотите взаимодействовать с базой данных только для временных тестов. В этом случае часть вашего автоматического тестирования будет включать в себя код для создания тестовой базы данных с нуля, затем запустить тесты, а затем уничтожить тестовую базу данных. Опять же, идея заключается в том, чтобы не иметь внешних побочных эффектов. Вероятно, есть несколько способов сделать это, и я недостаточно знаком с основами модульного тестирования, чтобы действительно дать конкретное предложение. Но если вы используете тестирование, встроенное в Visual Studio, то, возможно, Проект базы данных Visual Studio будет полезен.

1 голос
/ 25 марта 2012

Ваш вопрос слишком общий. Обычно вы должны убирать после каждого теста. Обычно вы не можете полагаться на то, что все тесты всегда выполняются в одном и том же порядке, и вы должны быть уверены в том, что находится в вашей базе данных. Для общей настройки или очистки большинство структур модульных тестов предоставляют методы setUp и tearDown, которые вы можете переопределить и будут вызываться автоматически. Я не знаю, как это работает в C #, но e. г. в JUnit (Java) у вас есть эти методы.

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

0 голосов
/ 04 августа 2017

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

Надеюсь, это кому-нибудь пригодится, когда-нибудь.

    [Test]
    public void Collates_Blah_As_Blah()
    {
        Assert.False(SINGLETON.Collection.Any());

        for (int i = 0; i < 2; i++)
            Assert.That(PROCESS(ValidRequest) == Status.Success);

        try
        {
            Assert.AreEqual(1, SINGLETON.Collection.Count);
        }
        finally
        {
            SINGLETON.Collection.Clear();
        }
    }

Блок finally будет выполняться независимо от того, прошло ли утверждение или нет, он также не представляет риск ложных проходов - что вызовет перехват!

0 голосов
/ 26 марта 2012

mbUnit имеет очень удобный атрибут Rollback, который очищает базу данных после завершения теста. Однако вам необходимо настроить DTC (координатор распределенных транзакций), чтобы иметь возможность его использовать.

0 голосов
/ 25 марта 2012

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

AnПодход, который я использовал при написании интеграционных тестов, заключается в том, чтобы тесты выполнялись на базе данных, отличной от базы данных приложения.Я склонен перестраивать тестовую базу данных в качестве предварительного условия для каждого запуска теста.Таким образом, вам не нужна детальная схема очистки для каждого теста, так как каждый тестовый прогон получает чистый лист между запусками.Большую часть своей разработки я выполнял с использованием сервера SQL, но в некоторых случаях я выполнял свои тесты на базе данных SQL Compact Edition, которую быстро и эффективно перестраивать между запусками.

0 голосов
/ 25 марта 2012

Это зависит от того, что вы на самом деле тестируете.Глядя на комментарии, я бы сказал да , но, кстати, сложно вычесть взгляд на комментарии.Очистка объекта, который вы только что вставили, на практике сбрасывает состояние теста.Поэтому, если вы выполняете очистку, вы начинаете тестировать систему очистки.

0 голосов
/ 25 марта 2012

В этом случае вам придется выполнить ручную очистку. Т.е. противоположность порождает некоторые объекты в БД.

Альтернативой является использование инструментов Mocking, таких как Rhino Mocks, чтобы база данных была просто базой данных в памяти

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...