Я создал фреймворк для модульного теста для c ++, который я хочу портировать на C позже, и столкнулся с проблемой, когда модульный тест просто не запускается. Модульные тесты создаются в файлах .cpp, и только один файл .cpp должен запускать все тесты.
Чтобы немного упростить, вот как обычно создается тест:
#define UNIT_TEST_IMPL // or whatever
#include "unit_test.hpp"
int main()
{
for(const auto& test : tests_queue)
test->run();
return 0;
}
#pragma once
struct Base
{
protected:
Base() = delete;
Base(Base* ptr);
public:
virtual void run() = 0;
};
#if defined(UNIT_TEST_IMPL)
#include <vector>
std::vector<Base*> tests_queue;
Base::Base(Base* ptr)
{
tests_queue.push_back(ptr);
}
#endif
#include "unit_test.hpp"
#include <iostream>
struct Test : Base
{
Test()
: Base(this)
{}
void run() override
{
std::cout << "new test" << std::endl;
}
};
struct Test2 : Base
{
Test2()
: Base(this)
{}
void run() override
{
std::cout << "new test2" << std::endl;
}
};
static Test test;
static Test2 test2;
Вопрос в том, почему не запускаются тесты, определенные в test.cpp (если я создаю тесты в файле main.cpp, они работают отлично)? Я предполагаю, что проблема заключается в том, как я храню базовые указатели, но я не знаю.
Компилятор g ++ 6.4.0
статическое-инициализация-порядок-фиаско в действии:
Порядок инициализации глобальных единиц перевода не указан, поэтому если test
, test2
создаются перед tests_queue
более поздняя инициализация уничтожит регистрацию.
Одно из возможных исправлений:
#pragma once
struct Base
{
protected:
Base();
public:
virtual ~Base() = default;
virtual void run() = 0;
};
#if defined(UNIT_TEST_IMPL) // Should be defined only once.
#include <vector>
std::vector<Base*>& get_tests_queue()
{
static std::vector<Base*> tests_queue;
return tests_queue;
}
Base::Base()
{
get_tests_queue().push_back(this);
}
#endif
поэтому ваш main.cpp будет:
#define UNIT_TEST_IMPL // or whatever
#include "unit_test.hpp"
int main()
{
for(const auto& test : get_tests_queue())
test->run();
}
Ваши юнит-тесты будут неизменными:
#include "unit_test.hpp"
#include <iostream>
struct Test : Base
{
void run() override { std::cout << "new test" << std::endl; }
};
struct Test2 : Base
{
void run() override { std::cout << "new test2" << std::endl; }
};
static Test test;
static Test2 test2;
Мои ставки переходят в ту или иную форму Статическая инициализация Fiasco
Не уверен насчет этого, но вы не можете рассчитывать на глобальные переменные (переменные Test1 и Test2, определенные в test.cpp, инициализируются до запуска Main.
Как правило, избегайте глобальных переменных, если можете (и в этом случае, вероятно, можете).
Но в основном ваша программа не должна делать никаких предположений о порядке инициализации глобальных / статических переменных. Здесь вы предполагаете, что две глобальные переменные инициализируются перед главной. Возможно, они не