2008-08-14 3 views
22

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

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

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

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

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

+0

Я действительно сталкиваюсь с этой же проблемой, и ни один из нижеприведенных ответов не является удовлетворительным. Конечно, вы можете тестировать фрагменты генератора кода. Проблема в том, как вы знаете, что сгенерированный код верен, т. Е. Нет регрессий или чего-то подобного, и поэтому как вы пишете автоматические тесты для сгенерированного кода (независимо от того, называются ли они модулями или интеграционными тестами)? – 2009-11-30 15:58:39

+1

@ Джеймс: нет легкого ответа ... Я только что перечитал этот вопрос, и ответы и все проблемы, которые у меня были в то время, наводнили. Я могу дать этот еще один выстрел в ближайшие недели, потому что время от времени я получаю различные регрессии, и все это становится все более и более важным для их обнаружения. – jkp 2009-11-30 21:46:58

ответ

12

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

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

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

0

Да, результаты ТОЛЬКО важны. Реальная задача - создать фреймворк, который позволяет вашему сгенерированному коду работать независимо ... тратить там время.

0

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

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

4

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

0

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

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

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

0

Единичное тестирование - это просто тестирование конкретного устройства. Поэтому, если вы пишете спецификацию для класса 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, вы можете модульного тестирования только система а, не имея реальной функциональности выполняется какой-либо другой системы. Это модульное тестирование.

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

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

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

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

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

0

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

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

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

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

Большая часть состоит в том, что он действительно проверяет вашу систему от конца до конца, от генерации кода до того, что вы на самом деле генерируете. Как только тест терпит неудачу, его легко отследить обратно туда, где генератор сломался.

Это очень мило.

Удачи вам!

Смежные вопросы