модульные тесты в C++

Что ж, мне немного любопытно, поэтому я сам проверил 3 сразу после того, как задал вопрос; -)

Хорошо, это не очень серьезный обзор, но вот что я могу сказать:

Я опробовал инструменты с настройками по умолчанию (это важно, потому что вы можете выбрать правила проверки) на следующем скрипте:

#!/usr/local/bin/python
# by Daniel Rosengren modified by e-satis

import sys, time
stdout = sys.stdout

BAILOUT = 16
MAX_ITERATIONS = 1000

class Iterator(object) :

    def __init__(self):

        print 'Rendering...'
        for y in xrange(-39, 39): 
            stdout.write('\n')
            for x in xrange(-39, 39):
                if self.mandelbrot(x/40.0, y/40.0) :
                    stdout.write(' ')
                else:
                    stdout.write('*')


    def mandelbrot(self, x, y):
        cr = y - 0.5
        ci = x
        zi = 0.0
        zr = 0.0

        for i in xrange(MAX_ITERATIONS) :
            temp = zr * zi
            zr2 = zr * zr
            zi2 = zi * zi
            zr = zr2 - zi2 + cr
            zi = temp + temp + ci

            if zi2 + zr2 > BAILOUT:
                return i

        return 0

t = time.time()
Iterator() 
print '\nPython Elapsed %.02f' % (time.time() - t)

Как результат:

  • PyChecker хлопотно, потому что он собирает модуль для его анализа. Если вы не хотите, чтобы ваш код выполнялся (например, он выполняет SQL-запрос), это плохо.
  • PyFlakes должен быть облегченным. Действительно, он решил, что код был идеальным. Я ищу что-то довольно серьезное, поэтому я не думаю, что пойду на это.
  • PyLint был очень разговорчив и оценил код 3/10 (OMG, я грязный кодер!).

Сильные стороны PyLint:

  • Очень описательный и точный отчет.
  • Обнаружение некоторых запахов кода. Здесь он сказал мне оставить свой класс, чтобы написать что-то с функциями, потому что подход ОО был бесполезен в этом конкретном случае. Что-то, что я знал, но никогда не ожидал, что компьютер скажет мне :-p
  • Полностью исправленный код работает быстрее (без классов, без привязки ссылок ...).
  • Сделано французской командой. Хорошо, это не плюс для всех, но мне это нравится; -)

Минусы PyLint:

  • Некоторые правила действительно строгие , Я знаю, что вы можете изменить его, и по умолчанию он соответствует PEP8, но является ли таким преступлением писать «для x in seq»? Видимо, да, потому что вы не можете написать имя переменной, содержащее менее 3 букв. Я изменю это.
  • 1119 Очень, очень разговорчивый. Будьте готовы использовать свои глаза.

Исправленный скрипт (с ленивыми строками документов и именами переменных):

#!/usr/local/bin/python
# by Daniel Rosengren, modified by e-satis
"""
Module doctring
"""


import time
from sys import stdout

BAILOUT = 16
MAX_ITERATIONS = 1000

def mandelbrot(dim_1, dim_2):
    """
    function doc string
    """
    cr1 = dim_1 - 0.5
    ci1 = dim_2
    zi1 = 0.0
    zr1 = 0.0

    for i in xrange(MAX_ITERATIONS) :
        temp = zr1 * zi1
        zr2 = zr1 * zr1
        zi2 = zi1 * zi1
        zr1 = zr2 - zi2 + cr1
        zi1 = temp + temp + ci1

        if zi2 + zr2 > BAILOUT:
            return i

    return 0

def execute() :
    """
    func doc string
    """
    print 'Rendering...'
    for dim_1 in xrange(-39, 39): 
        stdout.write('\n')
        for dim_2 in xrange(-39, 39):
            if mandelbrot(dim_1/40.0, dim_2/40.0) :
                stdout.write(' ')
            else:
                stdout.write('*')


START_TIME = time.time()
execute()
print '\nPython Elapsed %.02f' % (time.time() - START_TIME)

РЕДАКТИРОВАТЬ:

Благодаря Рудигеру Вольфу я обнаружил pep8, что в точности соответствует его названию: соответствует PEP8. Он нашел несколько синтаксисов, которые PyLint не нашел. Но PyLint нашел материал, который не был специально связан с PEP8, но интересен. Оба инструмента интересны и дополняют друг друга.

В конце концов, я буду использовать оба, так как их действительно легко установить (через пакеты или setuptools), а выводимый текст так просто связать.

Чтобы дать вам небольшое представление об их выводе:

pep8 :

./python_mandelbrot.py:4:11: E401 multiple imports on one line
./python_mandelbrot.py:10:1: E302 expected 2 blank lines, found 1
./python_mandelbrot.py:10:23: E203 whitespace before ':'
./python_mandelbrot.py:15:80: E501 line too long (108 characters)
./python_mandelbrot.py:23:1: W291 trailing whitespace
./python_mandelbrot.py:41:5: E301 expected 1 blank line, found 3

PyLint :

************* Module python_mandelbrot
C: 15: Line too long (108/80)
C: 61: Line too long (85/80)
C:  1: Missing docstring
C:  5: Invalid name "stdout" (should match (([A-Z_][A-Z0-9_]*)|(__.*__))$)
C: 10:Iterator: Missing docstring
C: 15:Iterator.__init__: Invalid name "y" (should match [a-z_][a-z0-9_]{2,30}$)
C: 17:Iterator.__init__: Invalid name "x" (should match [a-z_][a-z0-9_]{2,30}$)

[...] and a very long report with useful stats like :

Duplication
-----------

+-------------------------+------+---------+-----------+
|                         |now   |previous |difference |
+=========================+======+=========+===========+
|nb duplicated lines      |0     |0        |=          |
+-------------------------+------+---------+-----------+
|percent duplicated lines |0.000 |0.000    |=          |
+-------------------------+------+---------+-----------+
5
задан frast 29 June 2009 в 20:59
поделиться

7 ответов

Обновление : См. и этот вопрос.

Я могу только ответить некоторые части здесь:

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

Если ваша производительность будет слишком сильно из-за этого, нет (эталонный тест!). Если ваше развитие сильно пострадает, нет (оцените дополнительные усилия). Если кажется, что это не имеет большого значения и поможет в долгосрочной перспективе и поможет вам с качеством, да.

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

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

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

Крупные рефакторинги по определению сильно меняют, включая тесты. Будьте счастливы, что они у вас есть, так как они тоже будут тестировать вещи после рефакторинга.

Если вы тратите больше времени на рефакторинг, чем на создание новых функций, возможно, вам стоит подумать еще немного, прежде чем писать код, чтобы найти лучшие интерфейсы, которые могут выдержать большее количество изменений. Кроме того, писать модульные тесты до того, как интерфейсы станут стабильными, - это боль, независимо от того, что вы делаете.

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

Надеюсь, это поможет.

5
ответ дан 13 December 2019 в 19:33
поделиться

Я обычно использовал макросы, #if и другие уловки препроцессора для «имитации» зависимостей с целью модульного тестирования на C и C ++, именно потому, что с за такие макросы мне не нужно платить за время выполнения, если код компилируется для производства, а не для тестирования. Не элегантно, но достаточно эффективно.

Что касается рефакторинга, он вполне может потребовать изменения тестов, когда они настолько велики и навязчивы, как вы описываете. Однако я не замечаю, что реорганизую так радикально и так часто.

3
ответ дан 13 December 2019 в 19:33
поделиться

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

Большая часть разработки, управляемой тестированием, - это выбор небольших атомарных частей кода, которые вряд ли будут изменены для модульного тестирования. Если вам приходится переписывать множество модульных тестов из-за значительных изменений логики, вам может потребоваться более детальное тестирование или изменение кода, чтобы он стал более стабильным. Стабильный дизайн не должен кардинально меняться со временем, и тестирование победило » Это поможет вам избежать массового рефакторинга. Однако, если все сделано правильно, тестирование может сделать так, чтобы при рефакторинге вещей вы могли быть более уверены в том, что рефакторинг прошел успешно, при условии, что есть некоторые тесты, которые не нужно менять.

1
ответ дан 13 December 2019 в 19:33
поделиться

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

1
ответ дан 13 December 2019 в 19:33
поделиться

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

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

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

Определенно нормально разрабатывать классы, которые будут более тестируемыми . В конце концов, это часть цели TDD.

Это связано со значительными накладными расходами во время выполнения из-за множества виртуальных вызовов вместо простых вызовов методов.

Практически в каждой компании есть некоторый список правил, которым должны следовать все сотрудники. . Невежественные компании просто перечисляют все хорошие качества, о которых они могут подумать («наши сотрудники эффективны, ответственны, этичны и никогда не сгибают углы»). Более умные компании действительно ранжируют свои приоритеты. Если кто-то придумывает неэтичный способ повышения эффективности, делает ли это компания? Лучшие компании не только печатают буклеты с указанием приоритетов, но они также следят за тем, чтобы менеджмент следовал за рейтингом.

Программа вполне может быть эффективной и легко тестируемой. Однако бывают случаи, когда вам нужно выбрать, что важнее. Это как-раз тот случай. Я не знаю, насколько важна эффективность для вас и вашей программы, но вы знаете. Итак, «вы бы предпочли медленную, хорошо протестированную программу или быструю программу без полного тестового покрытия?»

0
ответ дан 13 December 2019 в 19:33
поделиться

Это связано со значительными накладными расходами на время выполнения из-за множества виртуальных вызовов вместо простых вызовов методов.

Помните, что это только накладные расходы виртуального вызова, если вы обращаетесь к методу через указатель (или ссылку) на ваш интерфейс или объект. Если вы обращаетесь к методу через конкретный объект в стеке, он не будет иметь виртуальных накладных расходов и даже может быть встроен.

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

0
ответ дан 13 December 2019 в 19:33
поделиться

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

Я думаю, что можно нарушить зависимости, поскольку это приведет к улучшению интерфейсов.

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

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

Возможно, вы на самом деле не выполняете TDD, например:

  1. Создайте тест, который не проходит.
  2. Создайте код для прохождения теста
  3. Создайте другой тест, который сбой
  4. Исправьте код, чтобы он прошел оба теста
  5. Промойте и повторяйте, пока не убедитесь, что у вас достаточно тестов, показывающих, что должен делать ваш код

Эти шаги говорят, что вы должны вносить незначительные изменения, а не большие. . Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

Возможно, вы на самом деле не выполняете TDD, например:

  1. Создайте тест, который не проходит.
  2. Создайте код для прохождения теста
  3. Создайте другой тест, который не прошел
  4. Исправьте код, чтобы пройти оба теста
  5. Промойте и повторяйте, пока не почувствуете, что у вас достаточно тестов, показывающих, что должен делать ваш код.

Эти шаги говорят, что вы должны вносить незначительные изменения, а не большие. Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

Возможно, вы на самом деле не выполняете TDD, например:

  1. Создайте тест, который не проходит.
  2. Создайте код для прохождения теста
  3. Создайте другой тест, который не прошел
  4. Исправьте код, чтобы пройти оба теста
  5. Промойте и повторяйте, пока не почувствуете, что у вас достаточно тестов, показывающих, что должен делать ваш код.

Эти шаги говорят, что вы должны вносить незначительные изменения, а не большие. Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

  1. Создать тест, который не прошел
  2. Создать код для прохождения теста
  3. Создать другой тест, который не прошел
  4. Исправить код, чтобы пройти оба теста
  5. Промойте и повторяйте, пока не убедитесь, что у вас достаточно тестов, покажите, что должен делать ваш код

Эти шаги говорят, что вы должны делать небольшие изменения, а не большие. Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

  1. Создать тест, который не прошел
  2. Создать код для прохождения теста
  3. Создать другой тест, который не прошел
  4. Исправить код, чтобы пройти оба теста
  5. Промойте и повторяйте, пока не убедитесь, что у вас достаточно тестов, покажите, что должен делать ваш код

Эти шаги говорят, что вы должны делать небольшие изменения, а не большие. Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

Если вы останетесь с последним, вам не избежать больших рефакторингов. Никакой язык не спасет вас от этого, и C ++ будет худшим из них из-за времени компиляции, времени компоновки, плохих сообщений об ошибках и т. Д. И т. Д.

Я фактически работаю в реальном программном обеспечении, написанном на C ++ с ОГРОМНЫЙ устаревший код под ним. Мы используем TDD, и он действительно помогает развивать дизайн программного обеспечения.

1
ответ дан 13 December 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: