Skip to content

Latest commit

 

History

History
118 lines (97 loc) · 8.3 KB

README.md

File metadata and controls

118 lines (97 loc) · 8.3 KB

Юнит тесты

Оглавление

  1. Модульное тестирование
  2. Какие бывают тесты
  3. Что отличает хорошие юнит-тесты
  4. Фреймворки для юнит-тестов
  5. Какие кейсы покрывать
  6. Когда писать тесты
  7. Польза юнит тестов
  8. Недостатки
  9. Примеры и комментарии после проверки домашнего задания

Модульное тестирование, или юнит-тестирование (англ. unit testing) — процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы.

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

UI тесты - это тесты на уже готовый продукт, который тестируется посредством взаимодействия с интерфейсом пользователя. Это уже последняя стадия тестирования и чаще всего это ручное тестирование, но есть также фреймворки, которые позволяют автоматизировать этот процесс. В двух словах они эмулируют действия пользователя, например, нажатия мышкой.

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

Юнит тесты - это тесты на самые маленькие единицы программы: функции и классы.

image_5_1
Представленное изображение называется пирамидой тестирования. Это сбалансированная пирамида, т.е., в идеальном случае, так в количественном соотношении должны соотносится разные тесты в проекте. Почему именно так? Потому что, как изображено на картинке, время выполнения тестов вверху пирамиды сильно превышает время выполнения тестов внизу. Также количество затраченных усилий на развертывание тестового окружения растет снизу вверх.

  • простота
  • отдельный тест должен проверять какое-то одно условие
  • легкость в написании
  • быстрота выполнения
  • не зависят от других тестов или окружения

Gtest (googletest) – популярный фреймворк для юнит тестов на C++ (и для C)
Основные конструкции: EXPECT_EQ, EXPECT_TRUE, EXPECT_THROW (те же варианты с ASSERT_)

  • все пути выполнения
  • граничные случаи
  • в конце имплементации
  • в начале имплементации

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

  • Надежность
  • Лучшее понимание требований
  • Сопровождение
  • Улучшение дизайна
  • Документация
  • дополнительное время на написание
  • не все можно протестировать (по крайней мере сразу)
  • пишутся теми же, кто пишет тестируемый код
  • тесты тоже нуждаются в сопровождении
TEST(...)
{
    std::vector<int> arr = {11, 22};  // представьте что здесь вызов чего-то типо ReadPricesFromDb()
    ASSERT_EQ(arr.size(), 3);         // если бы здесь стоял EXCEPT
    EXPECT_EQ(arr[0], 11);            // то эта строчка бы выполнилась
    EXPECT_EQ(arr[1], 22);            // эта тоже выполнилась
    EXPECT_EQ(arr[2], 33);            //  а здесь вылетело бы исключение
}

TEST(...)                                          // и этот тест бы не выполнился
TEST(...)                                          // (этот тоже)

Так краще не робити, бо як потім зрозуміти який тест сфейлився

std::vector<std::pair<size_t, size_t>> cases_GetProductsAmount = {
    {GetProductsAmount(std::vector<int>(0), 10), 0},
    {GetProductsAmount(std::vector<int>(0), 0), 0},
    {GetProductsAmount(std::vector<int>(5, 1), 0), 0},
    {GetProductsAmount(std::vector<int>(5, 1), 10), 5},
    {GetProductsAmount(std::vector<int>(5, 1), 2), 2},
    {GetProductsAmount(std::vector<int>(0), -10), 0},
    {GetProductsAmount(std::vector<int>(5, 1), -10), 0}
};

TEST(Products, getProductsAmount_LimitValues)
{
    for(auto &c : cases_GetProductsAmount)    
        EXPECT_EQ(c.first, c.second);        
}

Якщо дуже хочеться, то можете юзати таку штуку:

using TestData = std::tuple<std::vector<int>, int, size_t>;
class GetProductsAmountTest : public testing::TestWithParam<TestData> {};
INSTANTIATE_TEST_SUITE_P(PricesAmounts, GetProductsAmountTest, testing::Values(
    TestData{ {}, 10, 0 },
    TestData{ {}, 0, 0 },
    TestData{ {1, 1, 1, 1, 1}, 0, 0 },
    TestData{ {1, 1, 1, 1, 1}, 10, 5 },
    TestData{ {1, 1, 1, 1, 1}, 2, 2},
    TestData{ {}, -10, 0},
    TestData{ {1, 1, 1, 1, 1}, -10, 0}
));

TEST_P(GetProductsAmountTest, Acceptance)
{
    auto [prices, money, amount] = GetParam();
    EXPECT_EQ(GetProductsAmount(prices, money), amount);
}