Тестируем тесты с PIT (мутационное тестирование)

Preview:

Citation preview

ТЕСТИРУЕМТЕСТЫС PIT

15 сентября 2016

Кто я?Евгений Барановский

Я здесь, потому что люблю Java, качественный код и спокойствие.

Связаться со мной меня можно через judzin.baranovsky@gmail.com.

План выступления

▷Текущие ограничения тестов

▷Мутационное тестирование

▷Мутационное тестирование с PIT

▷Проблемы мутационного

тестирования

▷Будет много кода

РАССМОТРИМ ПРИМЕРНет ничего лучше примера

public class TimeLine {

private int fetchCount;

public TimeLine(int fetchCount) {setFetchCount(fetchCount);

}

public void setFetchCount(int fetchCount) {if (fetchCount <= 0) {

throw new IllegalArgumentException("Count must be > 0");}

this.fetchCount = fetchCount;}

public int getFetchCount() {return fetchCount;

}

// Some service logic, omitted for simplicity......}

public class TimeLineTest {

private TimeLine timeLine;

@BeforeMethodprotected void setUp() {

timeLine = new TimeLine(10);}

@Testpublic void shouldUpdateFetchCount() {

int expected = 5;timeLine.setFetchCount(expected);

assertEquals(timeLine.getFetchCount(), expected);}

@Test(expectedExceptions = IllegalArgumentException.class)public void shouldNotAllowNegativeFetchCount() {

timeLine.setFetchCount(-10);}

}

Что же не так страдиционнымтестированием?

Уберем вызов

▷Успешно прошедшие тесты не

всегда правы.

▷Существует много критериев

покрытия кода.

▷И даже эти критерии могут быть

неточными.

Текущие ограничения

Это искусственный баг. Если набор тестов не в состоянии обнаружить мутацию, то он рассматривается как недостаточный.

Мутация

Мутант – это система с мутациями.

Мутант

в 1970х

Причины низкой популярности:– недостаток технологий и– недостаток вычислительных мощностей.

Мутационное тестированиебыло разработано еще

Итак, как работает мутационное тестирование?

1. Написать тест

2. Выбрать мутации

3. Получить мутантов

4. Прогнать тесты на мутантах

5. Пересмотреть тесты

6. Прогнать тесты снова

Итак, как работает мутационное тестирование?

2. Выбрать мутации

3. Получить мутантов

4. Прогнать тесты на мутантах

5. Пересмотреть тесты

6. Прогнать тесты снова

1. Написать тест

1. Для знакомого класса ...public class TimeLine {

private int fetchCount;

public TimeLine(int fetchCount) {setFetchCount(fetchCount);

}

public void setFetchCount(int fetchCount) {if (fetchCount <= 0) {

throw new IllegalArgumentException("Count must be > 0");}

this.fetchCount = fetchCount;}

public int getFetchCount() {return fetchCount;

}

// Some service logic}

... напишем такой тест@BeforeMethodprotected void setUp() {

timeLine = new TimeLine(10);}

@Testpublic void shouldUpdateFetchCount() {

int expected = 5;timeLine.setFetchCount(expected);

assertEquals(timeLine.getFetchCount(), expected);}

@Test(expectedExceptions = IllegalArgumentException.class)public void shouldNotAllowNegativeFetchCount() {

timeLine.setFetchCount(-10);}

Итак, как работает мутационное тестирование?

1. Написать тест

3. Получить мутантов

4. Прогнать тесты на мутантах

5. Пересмотреть тесты

6. Прогнать тесты снова

2. Выбрать мутации

2. Выбрать мутации для кодаpublic class TimeLine {

private int fetchCount;

public TimeLine(int fetchCount) {setFetchCount(fetchCount);

}

public void setFetchCount(int fetchCount) {if (fetchCount <= 0) {

throw new IllegalArgumentException("Count must be > 0");}

this.fetchCount = fetchCount;}

public int getFetchCount() {return fetchCount;

}

// Some service logic}

Можно удалить вызов

Можно инвертировать условие

Можно изменить границу условия

Итак, как работает мутационное тестирование?

1. Написать тест

2. Выбрать мутации

4. Прогнать тесты на мутантах

5. Пересмотреть тесты

6. Прогнать тесты снова

3. Получить мутантов

3. Получить мутантов системыpublic class TimeLine {

private int fetchCount;

public TimeLine(int fetchCount) {setFetchCount(fetchCount);

}

public void setFetchCount(int fetchCount) {if (fetchCount <= 0) {

throw new IllegalArgumentException("Count must be > 0");}

this.fetchCount = fetchCount;}

public int getFetchCount() {return fetchCount;

}

// Some service logic}

public TimeLine(int fetchCount) {}

3. Получить мутантов системыpublic class TimeLine {

private int fetchCount;

public TimeLine(int fetchCount) {setFetchCount(fetchCount);

}

public void setFetchCount(int fetchCount) {if (fetchCount <= 0) {

throw new IllegalArgumentException("Count must be > 0");}

this.fetchCount = fetchCount;}

public int getFetchCount() {return fetchCount;

}

// Some service logic}

if (fetchCount < 0) {

if (fetchCount > 0) {

Итак, как работает мутационное тестирование?

1. Написать тест

2. Выбрать мутации

3. Получить мутантов

5. Пересмотреть тесты

6. Прогнать тесты снова

4. Прогнать тесты на мутантах

4. Прогнать тесты на мутантах

Не покрыто... Как так-то?

Итак, как работает мутационное тестирование?

1. Написать тест

2. Выбрать мутации

3. Получить мутантов

4. Прогнать тесты на мутантах

6. Прогнать тесты снова

5. Пересмотреть тесты

5. Пересмотреть тесты@BeforeMethodprotected void setUp() {

timeLine = new TimeLine(10);}

@Testpublic void shouldSetTheConstructorFetchValue() {

assertEquals(timeLine.getFetchCount(), 10);}

@Testpublic void shouldUpdateFetchCount() {

int expected = 5;timeLine.setFetchCount(expected);

assertEquals(timeLine.getFetchCount(), expected);}

@Test(expectedExceptions = IllegalArgumentException.class)public void shouldNotAllowNegativeFetchCount() {

timeLine.setFetchCount(-10);}

Итак, как работает мутационное тестирование?

1. Написать тест

2. Выбрать мутации

3. Получить мутантов

4. Прогнать тесты на мутантах

5. Пересмотреть тесты

6. Прогнать тесты снова

6. Прогнать тесты снова

Мутационное тестирование

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

2. Выбрать мутацииКод автоматически анализируется и к нему подбираются мутации.

3. Получить мутантовЗвучит зловеще, но это тоже происходит автоматически.

4. Прогнать тесты на мутантахНикаких дополнительных действий от программиста тут не требуется – не зря же мы писали тесты на самом первом шаге.

5. Пересмотреть тесты

Если мы получили ошибки, либо имеющиеся тесты, либо настройки мутаций надо пересмотреть.

6. Прогнать тесты снова

Запустить мутационное тестирование снова, но уже с исправленными тестами/конфигурацией.

ИНСТРУМЕНТЫПочему PIT?

Jester

Nester

Pester

PIT

БАЙТ-КОД– Работает напрямую с байт-кодом.

ДОКУМЕНТАЦИЯ– Хорошая документация + открытый код.– http://www.pitest.org + GitHub

ПРОСТОТА– Подсчитывает обычное покрытие строк.– Поддерживает различные оптимизации.

PIT

PIT – Пример отчета

PIT – Пример отчета

PIT – Пример отчета

PIT – Пример отчета

PIT – Пример отчета

▷Стабильные:

– простые,

– но эффективные.

▷Нестабильные:

– мощные,

– но чреваты ошибками.

PIT – Виды мутаций

▷Изменение граничных условий

PIT – Стабильные мутации

if (a < b) {// if (a <= b)// do something

}

▷Изменение граничных условий

▷Инверсия условий

PIT – Стабильные мутации

if (a == b) {// if (a != b)// do something

}

▷Изменение граничных условий

▷Инверсия условий

▷Инверсия операторов

PIT – Стабильные мутации

if (a >>> b) {// if (a << b)// do something

}

▷Удаление условий

PIT – Нестабильные мутации

if (a == b) {// if (true)// do something

}

▷Удаление условий

▷Удаление вызова конструктора

PIT – Нестабильные мутации

public int foo() {// Object o = null;Object o = new Object();return o;

}

▷Maven

▷Gradle

▷SonarQube

PIT – Интеграция со сборкой

▷Правильно настраивать плагины

сборки.

▷Избегать сгенерированных

классов (например, JAXB).

▷Избегать конфликтов со

смежными инструментами

(например, Clover, Cobertura).

С PIT нужно:

▷Может занимать много времени

▷Сложней реализовать

▷Трудно применить к тестам

уровнем выше интеграционных

Проблемы мутационного тестирования

Спасибо за внимание!Вопросы?