Как мне выполнить модульное тестирование генератора кода?

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

Я разработал генератор кода, который переносит наш интерфейс python в наш код C ++ (сгенерированный с помощью SWIG) и генерирует код, необходимый для представления его как WebServices. Когда я разработал этот код, я сделал это, используя TDD, но я обнаружил, что мои тесты были хрупкими до чертиков. Поскольку каждый тест по сути хотел проверить, что для заданного бита входного кода (который является заголовком C ++) я получу заданный бит выходного кода, я написал небольшой движок, который считывает определения тестов из входных файлов XML и генерирует тест случаи из этих ожиданий.

Проблема в том, что я боюсь изменить код вообще. Это и тот факт, что сами юнит-тесты являются a: сложными и b: хрупкими.

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

У кого-нибудь есть опыт чего-то похожего на это, которым он хотел бы поделиться?

14.08.2008 13:59:21
Я на самом деле сталкиваюсь с этой же проблемой, и ни один из приведенных ниже ответов на самом деле не является удовлетворительным. Конечно, вы можете тестировать модули генератора кода. Проблема в том, как вы знаете, что сгенерированный код является правильным, то есть, что нет регрессий или чего-то подобного, и, следовательно, как вы пишете автоматизированные тесты для сгенерированного кода (называются ли они модульными или интеграционными тестами)?
James Kingsbery 30.11.2009 15:58:39
@ Джеймс: здесь нет простого ответа ... Я только что перечитал этот вопрос, и ответы и все проблемы, которые у меня были в то время, затопили. Я могу сделать это еще раз в ближайшие недели, потому что время от времени у меня заканчиваются различные регрессии, и становится все более и более важным обнаруживать их.
jkp 30.11.2009 21:46:58
Это большое массивное сравнение строк. Может быть проще с помощью AST
Nikos 8.02.2017 16:04:32
8 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

12
14.08.2008 14:04:25
Да, это все равно, что сравнивать одну массивную строку с другой, иметь дело с такими вещами, как: Ожидаемое значение равно: "{\" options \ ": но получил {" options ":
Nikos 8.02.2017 16:03:52

Да, результаты - ЕДИНСТВЕННАЯ вещь, которая имеет значение. Настоящая рутина - это написание фреймворка, который позволяет вашему сгенерированному коду работать независимо ... проводить там время.

0
14.08.2008 14:38:19

Если вы работаете в * nux, вы можете подумать о том, чтобы сбросить инфраструктуру unittest в пользу скрипта bash или make-файла. В Windows вы можете подумать о создании приложения / функции оболочки, которое запускает генератор, а затем использует код (как другой процесс) и тестирует его.

Третий вариант - создать код, а затем создать из него приложение, которое не содержит ничего, кроме юнит-теста. Опять же вам понадобится сценарий оболочки или еще много чего, чтобы запустить это для каждого ввода. Что касается того, как кодировать ожидаемое поведение, мне приходит в голову, что это может быть сделано почти так же, как и для кода C ++, просто используя сгенерированный интерфейс, а не C ++.

0
14.08.2008 15:46:01

Напомним, что «модульное тестирование» является лишь одним из видов тестирования. Вы должны быть в состоянии выполнить модульное тестирование внутренних частей вашего генератора кода. На самом деле вы смотрите на тестирование на уровне системы (регрессионное тестирование). Это не просто семантика ... есть разные образы мыслей, подходы, ожидания и т. Д. Это, безусловно, больше работы, но вам, вероятно, нужно укусить пулю и установить комплексный набор регрессионных тестов: исправленные файлы C ++ -> SWIG интерфейсы -> модули Python -> известный вывод. Вы действительно хотите проверить известный ввод (фиксированный код C ++) по отношению к ожидаемому выводу (что выходит из финальной программы Python). Непосредственная проверка результатов генератора кода была бы похожа на различие объектных файлов ...

5
14.08.2008 18:15:42

Хотелось бы отметить, что вы все еще можете добиться детального тестирования при проверке результатов: вы можете протестировать отдельные фрагменты кода, вложив их в некоторый код установки и проверки:

int x = 0;
GENERATED_CODE
assert(x == 100);

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

0
16.09.2008 09:41:03

Модульное тестирование - это просто тестирование конкретного модуля. Поэтому, если вы пишете спецификацию для класса A, идеально, если у класса A нет реальных конкретных версий классов B и C.

Хорошо, я заметил, что впоследствии тег для этого вопроса включает в себя C ++ / Python, но принципы те же:

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

Поскольку вышеупомянутая система A внедряет интерфейсы в системы B и C, вы можете выполнить модульное тестирование только системы A, не имея реальной функциональности, выполняемой какой-либо другой системой. Это юнит-тестирование.

Вот умный способ приблизиться к Системе от создания до завершения, с различной спецификацией Когда для каждой части поведения:

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate<string>();
        this._returnB = randomGenerator.Generate<string>();
        this._returnC = randomGenerator.Generate<string>();

        Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

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

0
19.05.2010 23:17:11

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

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

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

0
25.07.2010 23:56:06

Я считаю, что вам нужно больше тестировать то, что вы генерируете, чем то, как вы это генерируете.

В моем случае программа генерирует много типов кода (C #, HTML, SCSS, JS и т. Д.), Которые компилируются в веб-приложение. Лучший способ уменьшить количество ошибок регрессии - это протестировать само веб-приложение, а не тестировать генератор.

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

Так как мы генерируем его, мы также генерируем хорошую абстракцию в JS, которую мы можем использовать для программного тестирования приложения. Мы следовали за некоторыми идеями, изложенными здесь: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

Самое замечательное в том, что он действительно тестирует вашу систему от начала до конца, от генерации кода до того, что вы фактически генерируете. Если тест не пройден, его легко отследить до того места, где сломался генератор.

Это довольно мило.

Удачи!

0
20.07.2015 04:10:25