SlideShare a Scribd company logo
1 of 43
Download to read offline
Использование юнит-тестов для
повышения качества разработки
cppconf.ru - 2017
Ястребов Виктор
Разработчик компании “Тензор”
1. Характеристики хорошего юнит-теста
2. Подходы к созданию тестируемого кода
3. Виды поддельных объектов
4. Пример тестирования класса
5. Приемы создания хороших юнит-тестов
О чем будем говорить
2/43
Допущения
• Код упрощен
•Используется Google Test
https://github.com/google/googletest/tree/master/googletest
3/43
Внешняя зависимость
Взаимодействие есть, а контроля нет
4/43
Жесткий диск, время, база данных
Интеграционный тест VS юнит-тест
Полный контроль
над внешними зависимостями
Интеграционный тест Юнит тест
нет да
5/43
Признаки хорошего юнит-теста
Полный контроль над внешними зависимостями
Автоматизация
запуска
Результат
• стабилен
• повторим
• независим
• Малое время выполнения
• Простота чтения
6/43
Принцип наименования юнит-теста
Sum_ByDefault_ReturnsZero()
Sum_WhenCalled_CallsTheLogger()
[ИмяТестируемойРабочейЕденицы]_
[СценарийТеста]_
[ОжидаемыйРезультат]
7/43
TEST_F( CalculatorTest,
Sum_ByDefault_ReturnsZero() )
{
Calculator calc;
int last_sum = calc.Sum();
ASSERT_EQ( 0, last_sum );
}
Структура юнит-теста
1. Arrange
2. Act
3. Assert
8/43
Рабочие единицы тестируемого кода
• Возвращаемый результат
• Изменение состояния
системы
• Взаимодействие между
объектами
9/43
Рабочие единицы тестируемого кода
• Возвращаемый результат
• Изменение состояния
системы
Задача
распознавания
Задача
разделения
Разрыв
зависимости
• Взаимодействие между
объектами
10/43
Поддельные
объекты
(Fakes)
Поддельные реализации объектов
Fake-объект
Stub-объект
Задача
разделения
Mock-объект
Задача
распознавания
12/43
Stub-объект
Взаимоде
йствие
Тестовый код
Тестируемый код Stub
Взаимодействие
13/43
Stub-объект
Взаимоде
йствие
Тестовый код
Тестируемый код Mock
Взаимодействие
14/43
Разбор
на примере
Исходный код
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
16/43
Исходный код
class WebService {
public:
void LogError( std::string msg ) {
/* логика, включающая
работу с сетевым соединением*/
}
};
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
17/43
Исходный код
class WebService {
public:
void LogError( std::string msg ) {
/* логика, включающая
работу с сетевым соединением*/
}
};
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
class DatabaseManager {
public:
bool IsValid( std::string ename ) {
/* логика, включающая
операции чтения из базы данных*/
}
};
18/43
Исходный код
class WebService {
public:
void LogError( std::string msg ) {
/* логика, включающая
работу с сетевым соединением*/
}
};
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
2. Внешняя зависимость
1. Внешняя зависимость
class DatabaseManager {
public:
bool IsValid( std::string ename ) {
/* логика, включающая
операции чтения из базы данных*/
}
};
19/43
Разрыв зависимости от базы данных
База данных
IsValid( std::string ename )
IDatabaseManager
FakeDatabaseManager DatabaseManager
20/43
class DatabaseManager :
public IDatabaseManager {
public:
bool IsValid( std::string ename ) override {
/* сложная логика, включающая
операции чтения из базы данных*/
}
};
class IDatabaseManager {
public:
virtual bool IsValid( std::string ename ) = 0;
virtual ~IDatabaseManager() = default;
};
class FakeDatabaseManager :
public IDatabaseManager {
public:
bool WillBeValid;
FakeDatabaseManager( bool will_be_valid ) :
WillBeValid( will_be_valid ) {
}
bool IsValid( std::string ename ) override {
return WillBeValid;
}
};
Разрыв зависимости от базы данных
21/43
Использование stub
для разрыва зависимости
Параметризация
конструктора.
Вариант 1
Вместо конкретной реализации – интерфейс
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
class EntryAnalyzer {
public:
EntryAnalyzer() :
pDbManager( std::make_unique<DatabaseManager>() ) {
}
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == pDbManager->IsValid( ename ) ) {
return false;
}
return true;
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
WebService webService;
};
23/43
Внедрение зависимости
class EntryAnalyzer {
public:
EntryAnalyzer( std::unique_ptr<IDatabaseManager> &&p_db_mng ) :
pDbManager( std::move( p_db_mng ) ) {
}
bool Analyze( std::string ename ) {
...
if( false == pDbManager->IsValid( ename ) )
return false;
return true;
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
...
};
внедрение
зависимости
24/43
class EntryAnalyzer {
public:
EntryAnalyzer() : pDbManager(
std::make_unique<DatabaseManager>() ) {
}
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == pDbManager->IsValid( ename ) )
return false;
return true;
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
WebService webService;
};
class EntryAnalyzer {
public:
EntryAnalyzer() : pDbManager(
std::make_unique<DatabaseManager>() ) {
}
EntryAnalyzer(
std:: unique_ptr<IDatabaseManager> &&p_mng ) :
pDbManager( std::move( p_mng ) ) {
}
bool Analyze( std::string ename ) {
...
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
WebService webService;
};
25/43
Внедрение зависимости
Тестирование возвращаемого значения
class FakeDatabaseManager : public IDatabaseManager {
public:
bool WillBeValid;
FakeDatabaseManager( bool will_be_valid ) : WillBeValid( will_be_valid ) {
}
bool IsValid( std::string ename ) override {
return WillBeValid;
}
};
TEST_F( EntryAnalyzerTest, Analyze_ValidEntryName_ReturnsTrue )
{
EntryAnalyzer ea( std::make_unique<FakeDatabaseManager>( true ) );
bool result = ea.Analyze( "valid_entry_name" );
ASSERT_EQ( result, true );
}
26/43
Использование stub
для разрыва зависимости
Параметризация
конструктора.
Вариант 2
class EntryAnalyzer {
public:
EntryAnalyzer() :
pDbManager( DbMngFactory::Create() ) {
}
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == pDbManager->IsValid( ename ) )
return false;
return true;
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
WebService webService;
};
Использование фабрики
class EntryAnalyzer {
public:
EntryAnalyzer() :
pDbManager( std::make_unique<DatabaseManager>() ) {
}
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == pDbManager->IsValid( ename ) )
return false;
return true;
}
private:
std::unique_ptr<IDatabaseManager> pDbManager;
WebService webService;
};
28/43
Тестирование возвращаемого значения
class DbMngFactory {
public:
static std::unique_ptr<IDatabaseManager> Create() {
if( nullptr == pDbMng )
return std::make_unique<DatabaseManager>();
return std::move( pDbMng );
}
static void SetManager(
std::unique_ptr<IDatabaseManager> &&p_mng ) {
pDbMng = std::move( p_mng );
}
private:
static std::unique_ptr<IDatabaseManager> pDbMng;
};
TEST_F( EntryAnalyzerTest,
Analyze_ValidEntryName_ReturnsTrue )
{
DbMngFactory::SetManager(
std::make_unique<FakeDatabaseManager>( true ) );
EntryAnalyzer ea;
bool result = ea.Analyze( "valid_entry_name" );
ASSERT_EQ( result, true );
}
29/43
Использование stub
для разрыва зависимости
“Выделить и
переопределить”
Выделение зависимости
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
...
if( false == IsValid( ename ) )
return false;
return true;
}
protected:
bool IsValid( std::string ename ) {
return dbManager.IsValid( ename );
}
private:
DatabaseManager dbManager;
...
};
31/43
Переопределение зависимости
class TestingEntryAnalyzer :
public EntryAnalyzer {
public:
bool WillBeValid;
private:
bool IsValid( std::string ename ) override {
return WillBeValid;
}
};
наследование
внедрение
зависимости
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
...
if( false == IsValid( ename ) )
return false;
return true;
}
protected:
virtual bool IsValid( std::string ename ) {
return dbManager.EntryIsValid( ename );
}
private:
DatabaseManager dbManager;
...
};
тестируемый класс
32/43
Тестирование возвращаемого значения
TEST_F( EntryAnalyzerTest,
Analyze_ValidEntryName_ReturnsTrue)
{
TestingEntryAnalyzer ea;
ea.WillBeValid = true;
bool result = ea.Analyze( "valid_entry_name" );
ASSERT_EQ( result, true );
}
class TestingEntryAnalyzer :
public EntryAnalyzer {
public:
bool WillBeValid;
private:
bool IsValid( std::string ename ) override {
return WillBeValid;
}
};
33/43
Использование
Mock для разрыва
зависимости
Выделение зависимости
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
webService.LogError( "Error: " + ename );
return false;
}
if( false == dbManager.IsValid( ename ) )
return false;
return true;
}
private:
DatabaseManager dbManager;
WebService webService;
};
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
LogError( "Error: " + ename);
return false;
}
...
}
protected:
virtual void LogError( std::string err ) {
webService.LogError( err );
}
private:
...
WebService webService;
};
35/43
Переопределение зависимости
class EntryAnalyzer {
public:
bool Analyze( std::string ename ) {
if( ename.size() < 2 ) {
LogError( "Error: " + ename);
return false;
}
...
}
protected:
virtual void LogError( std::string err ) {
webService.LogError( err );
}
private:
DatabaseManager dbManager;
WebService webService;
};
class TestingEntryAnalyzer :
public EntryAnalyzer {
public:
TestingEntryAnalyzer(
std::shared_ptr<IWebService> p_service ) :
pWebService( p_service ) {
}
private:
void LogError( std::string err ) override {
pWebService->LogError( err );
}
std::shared_ptr<IWebService> pWebService;
};
36/43
Тестирование взаимодействия
TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer )
{
std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>();
TestingEntryAnalyzer ea( p_web_service );
bool result = ea.Analyze( "e" );
ASSERT_EQ( p_web_service->lastError, "Error: e" );
}
37/43
Тестирование взаимодействия
TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer )
{
std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>();
TestingEntryAnalyzer ea( p_web_service );
bool result = ea.Analyze( "e" );
ASSERT_EQ( p_web_service->lastError, "Error: e" );
}
class TestingEntryAnalyzer : public EntryAnalyzer {
public:
TestingEntryAnalyzer(
std::shared_ptr<IWebService> p_service ) :
pWebService( p_service ) {
}
private:
void LogError( std::string err ) override {
pWebService->LogError( err );
}
std::shared_ptr<IWebService> pWebService;
};
38/43
Тестирование взаимодействия
TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer )
{
std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>();
TestingEntryAnalyzer ea( p_web_service );
bool result = ea.Analyze( "e" );
ASSERT_EQ( p_web_service->lastError, "Error: e" );
}
class FakeWebService : public IWebService {
public:
void LogError( std::string error ) override {
lastError = error;
}
std::string lastError;
};
class TestingEntryAnalyzer : public EntryAnalyzer {
public:
TestingEntryAnalyzer(
std::shared_ptr<IWebService> p_service ) :
pWebService( p_service ) {
}
private:
void LogError( std::string err ) override {
pWebService->LogError( err );
}
std::shared_ptr<IWebService> pWebService;
};
39/43
Приемы создания
хороших
unit-тестов
Практические приемы
• Один тест - один результат работы
• Тестируем только для публичные методы
• Нет ветвления
• операторы: switch, if, else
• циклы: for, while, std::for_each
• Юнит тест - последовательность вызовов методов + assert
• Используем фабрики
41/43
Где почитать подробнее
• Roy Osherove “The art of unit testing”. 2nd edition
•Майкл Физерс “Эффективная работа
с унаследованным кодом”
•Кент Бек “Экстремальное программирование.
Разработка через тестирование”
42/43
Спасибо за внимание!
cppconf.ru - 2017
Ястребов Виктор
Разработчик компании “Тензор”
va.yastrebov@tensor.ru

More Related Content

What's hot

Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о BoostSergey Platonov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Sergey Platonov
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...Alexey Paznikov
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMSergey Platonov
 
Фитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеФитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеIlia Shishkov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...Alexey Paznikov
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыcorehard_by
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексYandex
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...Alexey Paznikov
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonovComputer Science Club
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptSergey Platonov
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and catscorehard_by
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...Alexey Paznikov
 
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Alexey Paznikov
 

What's hot (20)

Антон Полухин, Немного о Boost
Антон Полухин, Немного о BoostАнтон Полухин, Немного о Boost
Антон Полухин, Немного о Boost
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
ПВТ - осень 2014 - Лекция 6 - Атомарные операции. Внеочередное выполнение инс...
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
 
Фитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в формеФитнес для вашего кода: как держать его в форме
Фитнес для вашего кода: как держать его в форме
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
Современный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтерыСовременный статический анализ кода: что умеет он, чего не умели линтеры
Современный статический анализ кода: что умеет он, чего не умели линтеры
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, Яндекс
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и JavascriptСергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
Сергей Шамбир, Адаптация Promise/A+ для взаимодействия между C++ и Javascript
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
 
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
 

Similar to Использование юнит-тестов для повышения качества разработки

Unit test быстрый старт
Unit test быстрый стартUnit test быстрый старт
Unit test быстрый стартAntonio
 
Tdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiTdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiMichael Chernobrov
 
Организация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий КопачёвОрганизация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий КопачёвMail.ru Group
 
ASP.NET MVC - как построить по-настоящему гибкое веб-приложение
ASP.NET MVC - как построить по-настоящему гибкое веб-приложениеASP.NET MVC - как построить по-настоящему гибкое веб-приложение
ASP.NET MVC - как построить по-настоящему гибкое веб-приложениеAlexander Byndyu
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Andrey Rebrov
 
"CommonJS для браузера", Антон Шувалов, MoscowJS 15
"CommonJS для браузера", Антон Шувалов, MoscowJS 15"CommonJS для браузера", Антон Шувалов, MoscowJS 15
"CommonJS для браузера", Антон Шувалов, MoscowJS 15MoscowJS
 
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»Yandex
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй этоRoman Dvornov
 
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCАрхитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCDevDay
 
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17MoscowJS
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Выжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим ГопейВыжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим ГопейEatDog
 
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020OdessaJS Conf
 
Top 10 problems supporting Magento customers
Top 10 problems supporting Magento customersTop 10 problems supporting Magento customers
Top 10 problems supporting Magento customersaheadWorks
 
Пластилиновый код: как перестать кодить и начать жить
Пластилиновый код: как перестать кодить и начать житьПластилиновый код: как перестать кодить и начать жить
Пластилиновый код: как перестать кодить и начать житьMoscow.pm
 
Как программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуКак программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуAndreyGeonya
 
Js templating stepan_reznikov
Js templating stepan_reznikovJs templating stepan_reznikov
Js templating stepan_reznikovyaevents
 

Similar to Использование юнит-тестов для повышения качества разработки (20)

Unit test быстрый старт
Unit test быстрый стартUnit test быстрый старт
Unit test быстрый старт
 
Tdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chaiTdd webpack + testem + mocha + chai
Tdd webpack + testem + mocha + chai
 
Организация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий КопачёвОрганизация работы с API на Vue.js, Виталий Копачёв
Организация работы с API на Vue.js, Виталий Копачёв
 
ASP.NET MVC - как построить по-настоящему гибкое веб-приложение
ASP.NET MVC - как построить по-настоящему гибкое веб-приложениеASP.NET MVC - как построить по-настоящему гибкое веб-приложение
ASP.NET MVC - как построить по-настоящему гибкое веб-приложение
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
 
"CommonJS для браузера", Антон Шувалов, MoscowJS 15
"CommonJS для браузера", Антон Шувалов, MoscowJS 15"CommonJS для браузера", Антон Шувалов, MoscowJS 15
"CommonJS для браузера", Антон Шувалов, MoscowJS 15
 
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»
FrontTalks: Алексей Андросов (Яндекс), «Ошибки, которые мы любим»
 
Инструментируй это
Инструментируй этоИнструментируй это
Инструментируй это
 
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVCАрхитектура кода нового 2ГИС Web API или куда мы дели MVC
Архитектура кода нового 2ГИС Web API или куда мы дели MVC
 
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17
"Ractive.js — реактивные усы!" Ефим Соловьев, MoscowJS 17
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Выжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим ГопейВыжить с помощью ООП. Максим Гопей
Выжить с помощью ООП. Максим Гопей
 
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
'The best practices' by KONSTANTIN KULAKSYZ at OdessaJS'2020
 
Top 10 problems supporting Magento customers
Top 10 problems supporting Magento customersTop 10 problems supporting Magento customers
Top 10 problems supporting Magento customers
 
бегун
бегунбегун
бегун
 
Пластилиновый код: как перестать кодить и начать жить
Пластилиновый код: как перестать кодить и начать житьПластилиновый код: как перестать кодить и начать жить
Пластилиновый код: как перестать кодить и начать жить
 
Как программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногуКак программировать на JavaScript и не выстрелить себе в ногу
Как программировать на JavaScript и не выстрелить себе в ногу
 
бегун
бегунбегун
бегун
 
Js templating stepan_reznikov
Js templating stepan_reznikovJs templating stepan_reznikov
Js templating stepan_reznikov
 

Использование юнит-тестов для повышения качества разработки

  • 1. Использование юнит-тестов для повышения качества разработки cppconf.ru - 2017 Ястребов Виктор Разработчик компании “Тензор”
  • 2. 1. Характеристики хорошего юнит-теста 2. Подходы к созданию тестируемого кода 3. Виды поддельных объектов 4. Пример тестирования класса 5. Приемы создания хороших юнит-тестов О чем будем говорить 2/43
  • 3. Допущения • Код упрощен •Используется Google Test https://github.com/google/googletest/tree/master/googletest 3/43
  • 4. Внешняя зависимость Взаимодействие есть, а контроля нет 4/43 Жесткий диск, время, база данных
  • 5. Интеграционный тест VS юнит-тест Полный контроль над внешними зависимостями Интеграционный тест Юнит тест нет да 5/43
  • 6. Признаки хорошего юнит-теста Полный контроль над внешними зависимостями Автоматизация запуска Результат • стабилен • повторим • независим • Малое время выполнения • Простота чтения 6/43
  • 8. TEST_F( CalculatorTest, Sum_ByDefault_ReturnsZero() ) { Calculator calc; int last_sum = calc.Sum(); ASSERT_EQ( 0, last_sum ); } Структура юнит-теста 1. Arrange 2. Act 3. Assert 8/43
  • 9. Рабочие единицы тестируемого кода • Возвращаемый результат • Изменение состояния системы • Взаимодействие между объектами 9/43
  • 10. Рабочие единицы тестируемого кода • Возвращаемый результат • Изменение состояния системы Задача распознавания Задача разделения Разрыв зависимости • Взаимодействие между объектами 10/43
  • 16. Исходный код class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; 16/43
  • 17. Исходный код class WebService { public: void LogError( std::string msg ) { /* логика, включающая работу с сетевым соединением*/ } }; class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; 17/43
  • 18. Исходный код class WebService { public: void LogError( std::string msg ) { /* логика, включающая работу с сетевым соединением*/ } }; class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; class DatabaseManager { public: bool IsValid( std::string ename ) { /* логика, включающая операции чтения из базы данных*/ } }; 18/43
  • 19. Исходный код class WebService { public: void LogError( std::string msg ) { /* логика, включающая работу с сетевым соединением*/ } }; class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; 2. Внешняя зависимость 1. Внешняя зависимость class DatabaseManager { public: bool IsValid( std::string ename ) { /* логика, включающая операции чтения из базы данных*/ } }; 19/43
  • 20. Разрыв зависимости от базы данных База данных IsValid( std::string ename ) IDatabaseManager FakeDatabaseManager DatabaseManager 20/43
  • 21. class DatabaseManager : public IDatabaseManager { public: bool IsValid( std::string ename ) override { /* сложная логика, включающая операции чтения из базы данных*/ } }; class IDatabaseManager { public: virtual bool IsValid( std::string ename ) = 0; virtual ~IDatabaseManager() = default; }; class FakeDatabaseManager : public IDatabaseManager { public: bool WillBeValid; FakeDatabaseManager( bool will_be_valid ) : WillBeValid( will_be_valid ) { } bool IsValid( std::string ename ) override { return WillBeValid; } }; Разрыв зависимости от базы данных 21/43
  • 22. Использование stub для разрыва зависимости Параметризация конструктора. Вариант 1
  • 23. Вместо конкретной реализации – интерфейс class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; class EntryAnalyzer { public: EntryAnalyzer() : pDbManager( std::make_unique<DatabaseManager>() ) { } bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == pDbManager->IsValid( ename ) ) { return false; } return true; } private: std::unique_ptr<IDatabaseManager> pDbManager; WebService webService; }; 23/43
  • 24. Внедрение зависимости class EntryAnalyzer { public: EntryAnalyzer( std::unique_ptr<IDatabaseManager> &&p_db_mng ) : pDbManager( std::move( p_db_mng ) ) { } bool Analyze( std::string ename ) { ... if( false == pDbManager->IsValid( ename ) ) return false; return true; } private: std::unique_ptr<IDatabaseManager> pDbManager; ... }; внедрение зависимости 24/43
  • 25. class EntryAnalyzer { public: EntryAnalyzer() : pDbManager( std::make_unique<DatabaseManager>() ) { } bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == pDbManager->IsValid( ename ) ) return false; return true; } private: std::unique_ptr<IDatabaseManager> pDbManager; WebService webService; }; class EntryAnalyzer { public: EntryAnalyzer() : pDbManager( std::make_unique<DatabaseManager>() ) { } EntryAnalyzer( std:: unique_ptr<IDatabaseManager> &&p_mng ) : pDbManager( std::move( p_mng ) ) { } bool Analyze( std::string ename ) { ... } private: std::unique_ptr<IDatabaseManager> pDbManager; WebService webService; }; 25/43 Внедрение зависимости
  • 26. Тестирование возвращаемого значения class FakeDatabaseManager : public IDatabaseManager { public: bool WillBeValid; FakeDatabaseManager( bool will_be_valid ) : WillBeValid( will_be_valid ) { } bool IsValid( std::string ename ) override { return WillBeValid; } }; TEST_F( EntryAnalyzerTest, Analyze_ValidEntryName_ReturnsTrue ) { EntryAnalyzer ea( std::make_unique<FakeDatabaseManager>( true ) ); bool result = ea.Analyze( "valid_entry_name" ); ASSERT_EQ( result, true ); } 26/43
  • 27. Использование stub для разрыва зависимости Параметризация конструктора. Вариант 2
  • 28. class EntryAnalyzer { public: EntryAnalyzer() : pDbManager( DbMngFactory::Create() ) { } bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == pDbManager->IsValid( ename ) ) return false; return true; } private: std::unique_ptr<IDatabaseManager> pDbManager; WebService webService; }; Использование фабрики class EntryAnalyzer { public: EntryAnalyzer() : pDbManager( std::make_unique<DatabaseManager>() ) { } bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == pDbManager->IsValid( ename ) ) return false; return true; } private: std::unique_ptr<IDatabaseManager> pDbManager; WebService webService; }; 28/43
  • 29. Тестирование возвращаемого значения class DbMngFactory { public: static std::unique_ptr<IDatabaseManager> Create() { if( nullptr == pDbMng ) return std::make_unique<DatabaseManager>(); return std::move( pDbMng ); } static void SetManager( std::unique_ptr<IDatabaseManager> &&p_mng ) { pDbMng = std::move( p_mng ); } private: static std::unique_ptr<IDatabaseManager> pDbMng; }; TEST_F( EntryAnalyzerTest, Analyze_ValidEntryName_ReturnsTrue ) { DbMngFactory::SetManager( std::make_unique<FakeDatabaseManager>( true ) ); EntryAnalyzer ea; bool result = ea.Analyze( "valid_entry_name" ); ASSERT_EQ( result, true ); } 29/43
  • 30. Использование stub для разрыва зависимости “Выделить и переопределить”
  • 31. Выделение зависимости class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; class EntryAnalyzer { public: bool Analyze( std::string ename ) { ... if( false == IsValid( ename ) ) return false; return true; } protected: bool IsValid( std::string ename ) { return dbManager.IsValid( ename ); } private: DatabaseManager dbManager; ... }; 31/43
  • 32. Переопределение зависимости class TestingEntryAnalyzer : public EntryAnalyzer { public: bool WillBeValid; private: bool IsValid( std::string ename ) override { return WillBeValid; } }; наследование внедрение зависимости class EntryAnalyzer { public: bool Analyze( std::string ename ) { ... if( false == IsValid( ename ) ) return false; return true; } protected: virtual bool IsValid( std::string ename ) { return dbManager.EntryIsValid( ename ); } private: DatabaseManager dbManager; ... }; тестируемый класс 32/43
  • 33. Тестирование возвращаемого значения TEST_F( EntryAnalyzerTest, Analyze_ValidEntryName_ReturnsTrue) { TestingEntryAnalyzer ea; ea.WillBeValid = true; bool result = ea.Analyze( "valid_entry_name" ); ASSERT_EQ( result, true ); } class TestingEntryAnalyzer : public EntryAnalyzer { public: bool WillBeValid; private: bool IsValid( std::string ename ) override { return WillBeValid; } }; 33/43
  • 35. Выделение зависимости class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { webService.LogError( "Error: " + ename ); return false; } if( false == dbManager.IsValid( ename ) ) return false; return true; } private: DatabaseManager dbManager; WebService webService; }; class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { LogError( "Error: " + ename); return false; } ... } protected: virtual void LogError( std::string err ) { webService.LogError( err ); } private: ... WebService webService; }; 35/43
  • 36. Переопределение зависимости class EntryAnalyzer { public: bool Analyze( std::string ename ) { if( ename.size() < 2 ) { LogError( "Error: " + ename); return false; } ... } protected: virtual void LogError( std::string err ) { webService.LogError( err ); } private: DatabaseManager dbManager; WebService webService; }; class TestingEntryAnalyzer : public EntryAnalyzer { public: TestingEntryAnalyzer( std::shared_ptr<IWebService> p_service ) : pWebService( p_service ) { } private: void LogError( std::string err ) override { pWebService->LogError( err ); } std::shared_ptr<IWebService> pWebService; }; 36/43
  • 37. Тестирование взаимодействия TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer ) { std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>(); TestingEntryAnalyzer ea( p_web_service ); bool result = ea.Analyze( "e" ); ASSERT_EQ( p_web_service->lastError, "Error: e" ); } 37/43
  • 38. Тестирование взаимодействия TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer ) { std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>(); TestingEntryAnalyzer ea( p_web_service ); bool result = ea.Analyze( "e" ); ASSERT_EQ( p_web_service->lastError, "Error: e" ); } class TestingEntryAnalyzer : public EntryAnalyzer { public: TestingEntryAnalyzer( std::shared_ptr<IWebService> p_service ) : pWebService( p_service ) { } private: void LogError( std::string err ) override { pWebService->LogError( err ); } std::shared_ptr<IWebService> pWebService; }; 38/43
  • 39. Тестирование взаимодействия TEST_F( EntryAnalyzerTest, Analyze_TooShortEntryName_LogsErrorToWebServer ) { std::shared_ptr<FakeWebService> p_web_service = std::make_shared<FakeWebService>(); TestingEntryAnalyzer ea( p_web_service ); bool result = ea.Analyze( "e" ); ASSERT_EQ( p_web_service->lastError, "Error: e" ); } class FakeWebService : public IWebService { public: void LogError( std::string error ) override { lastError = error; } std::string lastError; }; class TestingEntryAnalyzer : public EntryAnalyzer { public: TestingEntryAnalyzer( std::shared_ptr<IWebService> p_service ) : pWebService( p_service ) { } private: void LogError( std::string err ) override { pWebService->LogError( err ); } std::shared_ptr<IWebService> pWebService; }; 39/43
  • 41. Практические приемы • Один тест - один результат работы • Тестируем только для публичные методы • Нет ветвления • операторы: switch, if, else • циклы: for, while, std::for_each • Юнит тест - последовательность вызовов методов + assert • Используем фабрики 41/43
  • 42. Где почитать подробнее • Roy Osherove “The art of unit testing”. 2nd edition •Майкл Физерс “Эффективная работа с унаследованным кодом” •Кент Бек “Экстремальное программирование. Разработка через тестирование” 42/43
  • 43. Спасибо за внимание! cppconf.ru - 2017 Ястребов Виктор Разработчик компании “Тензор” va.yastrebov@tensor.ru