Какие ошибки могут содержать мой код, даже если у меня 100% покрытие кода? - PullRequest
14 голосов
/ 17 февраля 2009

Какой тип ошибок может содержать мой код, даже если у меня 100% покрытие кода? Я ищу конкретные примеры или ссылки на конкретные примеры таких ошибок.

Ответы [ 21 ]

37 голосов
/ 17 февраля 2009

100% охват кода не так хорош, как можно подумать. Рассмотрим триальный пример:

double Foo(double a, double b)
{
    return a / b;
}

Даже одиночный модульный тест увеличит охват кода этого метода до 100%, но упомянутый модульный тест не скажет нам, какой код работает, а какой нет. Это может быть совершенно корректный код, но без тестирования крайних условий (например, когда b равно 0.0) модульный тест в лучшем случае не дает результатов.

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

Послушайте это для интересного обсуждения.

11 голосов
/ 17 февраля 2009

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

Более элегантный способ сделать то же самое - это нечто, называемое абстрактной интерпретацией. MSR (Microsoft Research) выпустила нечто под названием CodeContracts , основанное на абстрактной интерпретации. Проверьте также Pex , они действительно выделяют методы тестирования поведения приложений во время выполнения .

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

Покрытие кода не подразумевает хороших тестов

8 голосов
/ 17 февраля 2009

Мм? Думаю, какая-нибудь обычная логическая ошибка? Повреждение памяти, переполнение буфера, старый неправильный код, присваивание вместо проверки, список можно продолжить. Покрытие только в том, что оно позволяет вам знать, что все пути кода выполнены, а не то, что они правильные.

6 голосов
/ 17 февраля 2009

Поскольку я еще не упоминал об этом, я хотел бы добавить этот поток, в котором покрытие кода не сообщает вам, какая часть вашего кода не содержит ошибок.

Он только сообщает вам, какие части вашего кода гарантированно не проверены.

5 голосов
/ 17 февраля 2009

1. Проблемы с «пространством данных»

Ваш (плохой) код:

void f(int n, int increment)
{
  while(n < 500)
  {
    cout << n;
    n += increment;
  }
}

Ваш тест:

f(200,100);

Ошибка в реальном мире:

f(200,0);

Моя точка зрения: ваш тест может охватывать 100% строк вашего кода, но он (как правило) не будет охватывать все ваше возможное пространство входных данных, то есть набор всех возможных значений входных данных.

2. Проверка на свою ошибку

Другой классический пример - когда вы просто принимаете плохое решение в дизайне и проверяете свой код на своем собственном плохом решении.

например. В техническом документе написано «печатать все простые числа до n », и вы печатаете все простые числа до n , но , исключая n И ваши юнит-тесты проверяют вашу неправильную идею.

3. Неопределенное поведение

Используйте значение неинициализированных переменных, вызовите недопустимый доступ к памяти и т. Д., И ваш код имеет неопределенное поведение (в C ++ или любом другом языке, который предполагает «неопределенное поведение»). Иногда он пройдет ваши тесты, но в реальном мире рухнет.

...

3 голосов
/ 17 февраля 2009

Рассмотрим следующий код:

int add(int a, int b)
{
  return a + b;
}

Этот код может не реализовывать некоторые необходимые функциональные возможности (то есть не соответствовать требованиям конечного пользователя): «100% покрытие» не обязательно проверяет / обнаруживает функциональность, которая должна быть реализована, но которая не является.

Этот код может работать для некоторых, но не для всех диапазонов входных данных (например, когда a и b оба очень велики).

3 голосов
/ 17 февраля 2009

Покрытие кода обычно говорит только о том, сколько ветвей в функции покрыто. Обычно он не сообщает о различных путях, которые могут быть взяты между вызовами функций . Многие ошибки в программах происходят из-за неправильной передачи обслуживания от одного метода к другому, а не потому, что сами методы содержат ошибки. Все ошибки этой формы все еще могут существовать при 100% -ном покрытии кода.

3 голосов
/ 17 февраля 2009

В недавней статье IEEE Software "Две ошибки и безошибочное программное обеспечение: признание" Роберт Гласс утверждал, что в "реальном мире" есть больше ошибок, вызванных отсутствием логики или комбинаторики (которая не может быть защищенным средствами покрытия кода), чем логическими ошибками (которые могут).

Другими словами, даже при 100% покрытии кода вы все равно рискуете встретить ошибки такого рода. И лучшее, что вы можете сделать, - как вы уже догадались - сделать больше обзоров кода.

Ссылка на статью здесь , и я нашел приблизительное резюме здесь .

3 голосов
/ 17 февраля 2009

работает на моей машине

Многие вещи хорошо работают на локальном компьютере, и мы не можем гарантировать, что это работает на Staging / Production. Покрытие кода может не охватывать это.

3 голосов
/ 17 февраля 2009

Всегда могут быть исключения времени выполнения: заполнение памяти, не закрытие базы данных или других соединений и т. Д. *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...