Сегодня знаменательный день - первый день нового года. Сегодня Я собираюсь начать новую жизнь. Я собираюсь перестать есть жирную пищу, начать посещать фитнес-клуб и ... сегодня я собираюсь протестировать программы, которые я пишу. Я могу начать сразу после завершения последней строки программы или, что еще лучше, я могу писать тесты во время кодирования. И, возможно, в следующий раз я напишу тесты перед кодированием, на этапе проектирования. Я прочитал много литературы о том, как писать тесты, у меня есть единая система тестирования и идея нового класса. Так что давайте начнем.
Скажем, я хочу инкапсулировать неизменяемый буфер символов C с длиной в простой классconst_string
. Обоснование: класс строк, который не выделяет память и обеспечивает удобный доступ только для чтения к предварительно выделенному буферу символов. Я, наверное, хочуconst_string
иметь интерфейс, аналогичный классу std::string. Что я сделаю первым? В моей новой жизни я начну с написания тестового модуля для будущего класса
. Это будет выглядеть так:
#define BOOST_TEST_MODULE
const_string test
#include <boost/test/unit_test.hpp>
Теперь я могу скомпилировать его и связать с системой тестирования. Готово! У меня есть рабочая программа тестирования. Он пуст, поэтому, когда я запускаю программу, он производит следующий результат:
*** No errors detected
Что ж, сейчас самое время начать работу надconst_string
. Первое, что я думаю, было бы хорошо иметь, это конструкторы и тривиальные методы доступа. Первоначальная версия выглядит так:
class const_string {
public:
const_string();
const_string( std::string const& s )
const_string( char const* s );
const_string( char const* s, size_t length );
const_string( char const* begin, char const* end );
char const* data() const;
size_t length() const;
bool is_empty() const;
};
Теперь я могу написать первый тест-кейс — конструкторское тестирование — и добавить его в тестовый набор. Моя тестовая программа стала выглядеть так:
#define BOOST_TEST_MODULE
const_string test
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE
( constructors_test )
{
const_string cs0( "" );
BOOST_TEST
( cs0.length() == (size_t)0 );
BOOST_TEST
( cs0.is_empty() );
const_string cs01( NULL );
BOOST_TEST
( cs01.length() == (size_t)0 );
BOOST_TEST
( cs01.is_empty() );
const_string cs1( "test_string" );
BOOST_TEST
( std::strcmp( cs1.data(), "test_string" ) == 0 );
BOOST_TEST
( cs1.length() == std::strlen("test_string") );
std::string s( "test_string" );
const_string cs2( s );
BOOST_TEST
( std::strcmp( cs2.data(), "test_string" ) == 0 );
const_string cs3( cs1 );
BOOST_TEST
( std::strcmp( cs3.data(), "test_string" ) == 0 );
const_string cs4( "test_string", 4 );
BOOST_TEST
( std::strncmp( cs4.data(), "test", cs4.length() ) == 0 );
const_string cs5( s.data(), s.data() + s.length() );
BOOST_TEST
( std::strncmp( cs5.data(), "test_string", cs5.length() ) == 0 );
const_string cs_array[] = { "str1", "str2" };
BOOST_TEST
( cs_array[0] == "str1" );
BOOST_TEST
( cs_array[1] == "str2" );
}
Тест-кейс конструкторов предназначен для проверки простой характеристики классаconst_string
: способности правильно конструировать себя на основе различных аргументов. Для проверки этой особенности я использую такие характеристики построенного объекта, как данные, которые он содержит, и длину. Спецификация классаconst_string
не содержит каких-либо ожидаемых сбоев, поэтому, хотя конструктор может выйти из строя, если я передам указатель на недействительную память, проверка ошибок не выполняется (не может требовать того, что не было обещано :-)). Но для любого действительного ввода он должен работать. Поэтому я пытаюсь проверить конструкцию на пустую строку (1), строку NULL (2), обычную строку C (3), строку STL (4), конструкцию копии (5) и так далее. Ну а после исправления всех ошибок в реализации (вы пишете программы без ошибок с нуля?) Я могу пройти этот тест-кейс, и система тестирования дает мне следующий отчет:
Running 1 test case...
*** No errors detected
Поощряю вас двигаться дальше и добавлять больше методов доступа:
class const_string {
public:
char operator[]( size_t index ) const;
char at( size_t index ) const;
};
Я добавил новую функцию — мне нужен новый тест-кейс, чтобы проверить его. В результате мой тестовый набор стал выглядеть так:
#define BOOST_TEST_MODULE
const_string test
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE
( constructors_test )
{
}
BOOST_AUTO_TEST_CASE
( data_access_test )
{
const_string cs1( "test_string" );
BOOST_TEST
( cs1[(size_t)0] == 't' );
BOOST_TEST
( cs1[(size_t)4] == '_' );
BOOST_TEST
( cs1[cs1.length()-1] == 'g' );
BOOST_TEST
( cs1[(size_t)0] == cs1.at( 0 ) );
BOOST_TEST
( cs1[(size_t)2] == cs1.at( 5 ) );
BOOST_TEST
( cs1.at( cs1.length() - 1 ) == 'g' );
BOOST_CHECK_THROW( cs1.at( cs1.length() ), std::out_of_range );
}
В тесте data_access_test я пытаюсь проверить правильность доступа к символам классаconst_string
. В то время как тесты (1) проверяют действительный доступ с помощьюconst_string::оператор[]
и тест (2) проверяет действительный доступ с помощью методаconst_string::в[]
, есть еще одна вещь для тестирования. Спецификация методаconst_string::at()
содержит подтверждение для несвязанного доступа. Этот тест (3) предназначен для проверки того, что валидация работает. Тестирование кода проверки и обработки ошибок является важной частью модульного тестирования и не должно быть оставлено на стадии производства. Тестовый тест data_access_test прошел, и я готов к следующему шагу.
Продолжая свои усилия, я могу завершить классconst_string
(см.Listing 1const_string.hpp
) и модуль тестирования для него (см.Listing 2const_string_test.cpp
), который проверяет все функции, представленные в спецификации классаconst_string
.
Ну, я на шаг ближе к выполнению моей новогодней резолюции (мы должны увидеть об этом фитнес-клубе в следующий раз). Что насчет тебя? Ваши тестовые привычки могут быть немного другими. Вы можете начать с разработки класса / библиотеки, а затем в какой-то момент начать писать тестовые случаи на основе функций. Или вы можете, учитывая подробную спецификацию для будущего продукта, в том числе ожидаемые интерфейсы, сразу же начать с написания всех тестовых случаев (или это может быть другой человек, пока вы работаете над реализацией одновременно). В любом случае у вас не должно возникнуть никаких проблем с использованием средств, предоставляемых Boost. Тестовый блок тестовой структуры и, надеюсь, сможет написать стабильный, пуленепробиваемый код. И что еще более важно, это ваша уверенность в способности вносить изменения любой сложности без длительного регрессионного тестирования всего вашего продукта. Ваш тестовый модуль и модуль тестирования останутся позади, чтобы помочь вам с любыми случайными ошибками.