Конструктор для тестового класса в Google Test - PullRequest
0 голосов
/ 01 марта 2019
namespace {

using namespace std;
// Declare the google test case
class InjectorTest : public ::testing::Test {

std::shared_ptr<MyData>> m_reader;
public:
    static void SetUpTestCase() {

    }
    static void TearDownTestCase() {

    }

    InjectorTest()
    {
        //Create Reader code here !!!!
        m_reader = CreateReader();

    }
    std::shared_ptr<DDSTopicReader<MyData>> getXMLReader() {

        return m_reader;
    }
};

TEST_F(InjectorTest, CreateReaderAndGetTopic) {
        auto reader = getXMLReader();
        std::string topic_name = reader->getTopicName();
        EXPECT_EQ(topic_name, "test_topic");
}
}; // anonymous namespace

Мои вопросы

1) Когда я запускаю контрольный пример CreateReaderAndGetTopic, вызывается ли Constructor InjectorTest перед выполнением контрольного примера?Или он должен вызываться явно?

2) Должен ли мой класс InjectorTest иметь конструктор или код внутри конструктора должен быть перемещен в SetUpTestCase.

Ответы [ 2 ]

0 голосов
/ 06 марта 2019

Сначала отметим, что статические члены класса SetUpTestCase и TearDownTestCase были переименованы соответственно в SetUpTestSuite и TearDownTestSuite начиная с googletest v1.8.См. Остерегайтесь номенклатуры

Они сделали это, потому что имена SetUpTestCase и TearDownTestCase вводили в заблуждение.Они предлагают функции, которые будут запускаться в каждом случае теста , который получен из класса фикстуры, в котором они определены, а это не так.SetUpTestCase фактически выполнялся до первого из всех тестов, полученных из приспособления (и остается, если у вас более старая установка googletest), а TearDownTestCase запускался после последнего извсе тесты, полученные из прибора.

Начиная с googletest 1.8, все тесты, которые были получены из прибора - который мы естественным образом назвали бы тестовыми примерами - собраны вместекак набор тестов ;таким образом, функция настройки, которая запускается до того, как все они теперь называются SetUpTestSuite, а функция разрыва, которая выполняется после них, называется TearDownTestSuite.Я буду придерживаться новых улучшенных имен, так как у меня последняя версия googletest.

Посмотрите на этот код gtest, который демонстрирует порядок вызова конструктора и деструктора прибора, SetUp и TearDown, SetUpTestSuite и TearDownTestSuite:

gtester.cpp

#include <gtest/gtest.h>
#include <iostream>

struct fixture : ::testing::Test
{
    fixture() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    ~fixture() override {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    void SetUp() override {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    void TearDown() override {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    static void SetUpTestSuite() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    static void TearDownTestSuite() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

TEST_F(fixture, One) {
    ASSERT_EQ(1,1);
}

TEST_F(fixture, Two) {
    ASSERT_NE(1,0);
}

TEST_F(fixture, Three) {
    ASSERT_LT(1,2);
}

Вы можете получить полезный взгляд под капотом, если вы просто предварительно обработаете этот файл (и желательно, pipe)через симпатичный принтер) и посмотрите, что получится:

$   g++ -E -P gtester.cpp | clang-format > gtester.ii

В gtester.ii вы сможете найти:

...
class fixture_One_Test : public fixture {
public:
  fixture_One_Test() {}
private:
  virtual void TestBody();
  static ::testing::TestInfo *const test_info_ __attribute__((unused));
  fixture_One_Test(fixture_One_Test const &) = delete;
  void operator=(fixture_One_Test const &) = delete;
};
::testing::TestInfo *const fixture_One_Test::test_info_ =
    ::testing::internal::MakeAndRegisterTestInfo(
        "fixture", "One", nullptr, nullptr,
        ::testing::internal::CodeLocation("gtester.cpp", 31),
        (::testing::internal::GetTypeId<fixture>()),
        ::testing::internal::SuiteApiResolver<fixture>::GetSetUpCaseOrSuite(),
        ::testing::internal::SuiteApiResolver<
            fixture>::GetTearDownCaseOrSuite(),
        new ::testing::internal::TestFactoryImpl<fixture_One_Test>);
void fixture_One_Test::TestBody() {
  switch (0)
  case 0:
  default:
    if (const ::testing::AssertionResult gtest_ar =
            (::testing::internal::EqHelper<
                decltype(::testing::internal::IsNullLiteralHelper(
                    1, ::testing::internal::TypeIsValidNullptrConstant<decltype(
                           1)>()))::value>::Compare("1", "1", 1, 1)))
      ;
    else
      return ::testing::internal::AssertHelper(
                 ::testing::TestPartResult::kFatalFailure, "gtester.cpp", 32,
                 gtest_ar.failure_message()) = ::testing::Message();
}
...

Вот какой тестовый пример:

TEST_F(fixture, One) {
    ASSERT_EQ(1,1);
}

расширяется до после предварительной обработки.Когда googletest запускает тестовый набор fixture.One:

  • Он создает новый экземпляр fixture_One_Test, который по наследству является новым экземпляром fixture.
  • Base fixture конструктор вызывается.
  • Производный fixture_One_Test конструктор вызывается.(Но он пуст.)
  • Googletest вызывает fixture::Setup() через fixture_One_Test экземпляр.
  • Он вызывает fixture_One_Test::TestBody() через fixture_One_Test экземпляр, который выполняет полезную нагрузку тестового примера..
  • Он вызывает вызовы fixture::TearDown() через экземпляр fixture_One_Test.
  • Уничтожает экземпляр fixture_One_Test, который -
  • Вызывает деструктор fixture_One_Test, которыйпросто -
  • Вызывает деструктор fixture.

Аналогично для fixture_Two_Test и fixture_Three_Test.

И как раз перед всем этим, googletest вызывает fixture::SetupTestSuite, если он определен.

И после всего этого googletest вызывает fixture::TearDownTestSuite, если он определен.

Теперь давайте посмотрим, что в действии:

$ g++ -Wall -Wextra -o gtester gtester.cpp -lgtest_main -lgtest -pthread
$ ./gtester 
Running main() from /home/imk/Downloads/googletest-master/googletest/src/gtest_main.cc
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from fixture
static void fixture::SetUpTestSuite()
[ RUN      ] fixture.One
fixture::fixture()
virtual void fixture::SetUp()
virtual void fixture::TearDown()
virtual fixture::~fixture()
[       OK ] fixture.One (0 ms)
[ RUN      ] fixture.Two
fixture::fixture()
virtual void fixture::SetUp()
virtual void fixture::TearDown()
virtual fixture::~fixture()
[       OK ] fixture.Two (0 ms)
[ RUN      ] fixture.Three
fixture::fixture()
virtual void fixture::SetUp()
virtual void fixture::TearDown()
virtual fixture::~fixture()
[       OK ] fixture.Three (0 ms)
static void fixture::TearDownTestSuite()
[----------] 3 tests from fixture (0 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 3 tests.

SetUpTestSuite() и TearDownTestSuite() каждый запускается только один раз.Конструктор, деструктор, SetUp() и TearDown(), каждый из которых запускается 3 раза = количество тестов в наборе тестов.

Итак, на ваши два вопроса:

Когда явыполнить тестовый сценарий CreateReaderAndGetTopic вызывается ли конструктор InjectorTest перед выполнением тестового примера?Или он должен вызываться явно?

Конструктор InjectorTest обязательно вызывается для создания экземпляра класса CreateReaderAndGetTopic_InjectorTest_Test, который Googletest делает для вас.Он вызывается перед SetUp(), который вызывается перед TestBody(), который является полезной нагрузкой тестового примера.

Если у моего класса InjectorTest есть конструктор ...

InjectorTest будет иметь конструктор, либо по умолчанию, либо как вы определили;поэтому вы задаетесь вопросом, следует ли вам определять один.

Вы должны определить один, если у вас есть что-то, что нужно сделать заново при подготовке к каждому тестуcase, и это лучше сделать перед SetUp() для каждого тестового случая.

... или код внутри конструктора должен быть перемещен в SetUpTestCase?

Код, который вы имеете внутри конструктора:

m_reader = CreateReader();

должен быть перемещен в SetUpTestCase - иначе SetUpTestSuite - если это что-то, что не нужно сделать зановов подготовке к каждому тесту, но это должно бытьвыполняется один раз непосредственно перед во всех тестовых случаях InjectorTest.

В существующем состоянии вы не можете переместить этот код конструктора в SetUpTestSuite, поскольку он инициализирует нестатический член класса InjectorTest, m_reader.Но вам действительно нужно или вы хотите, чтобы CreateReader() создавал новый XMLReader для каждого теста?Только вы можете решить.

Если вы хотите создать новое средство чтения для каждого тестового случая, тогда перед вами стоит выбор - в конструкторе или в SetUp().Здесь вы можете руководствоваться Googletest FAQ Должен ли я использовать конструктор / деструктор тестового устройства или SetUp () / TearDown ()?

0 голосов
/ 01 марта 2019

Если вы хотите убедиться, что ваши тестовые случаи в приборе имеют одинаковую настройку, вы должны реализовать методы SetUp и TearDown (обратите внимание на случай)

Ваш InjectorTest только получаетсоздается один раз, но шаг SetUp (соответственно шаг TearDown) будет выполняться до (соответственно после) выполнения вашего контрольного примера.

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

Подводя итог, вот последовательность операций, которые будут выполняться:

  1. InjectorTest::InjectorTest(): конструктор для испытаний
  2. Для каждого теста:
    1. InjectorTest::SetUp(): настройка для теста
    2. Тествыполняется (в вашем примере InjectorTest::CreateReaderAndGetTopic)
    3. InjectorTest::TearDown(): установка отменена для следующего случая
  3. InjectorTest::~InjectorTest(): уничтожение объекта фиксации
...