Как мне проверить модуль генератора кода? - PullRequest
23 голосов
/ 14 августа 2008

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

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

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

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

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

Ответы [ 8 ]

12 голосов
/ 14 августа 2008

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

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

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

5 голосов
/ 14 августа 2008

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

0 голосов
/ 20 июля 2015

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

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

* 1004.

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

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

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

Удачи!

0 голосов
/ 26 июля 2010

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

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

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

0 голосов
/ 20 мая 2010

Юнит-тестирование - это просто тестирование конкретного юнита. Поэтому, если вы пишете спецификацию для класса 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 голосов
/ 16 сентября 2008

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

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

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

0 голосов
/ 14 августа 2008

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

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

0 голосов
/ 14 августа 2008

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

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