2008-09-18 8 views
19

Я ищу инструмент, который может принимать модульное тестирование, какСоздание классов автоматически из модульных тестов?

IPerson p = new Person(); 
p.Name = "Sklivvz"; 
Assert.AreEqual("Sklivvz", p.Name); 

и генерировать автоматически, соответствующий класс заглушки и интерфейс

interface IPerson   // inferred from IPerson p = new Person(); 
{ 
    string Name 
    { 
     get;    // inferred from Assert.AreEqual("Sklivvz", p.Name); 
     set;    // inferred from p.Name = "Sklivvz"; 
    } 
} 

class Person: IPerson  // inferred from IPerson p = new Person(); 
{ 
    private string name; // inferred from p.Name = "Sklivvz"; 

    public string Name // inferred from p.Name = "Sklivvz"; 
    { 
     get 
     { 
      return name; // inferred from Assert.AreEqual("Sklivvz", p.Name); 
     } 
     set 
     { 
      name = value; // inferred from p.Name = "Sklivvz"; 
     } 
    } 

    public Person()  // inferred from IPerson p = new Person(); 
    { 
    } 
} 

Я знаю, ReSharper и Visual Studio сделать некоторые но мне нужен полный инструмент - командная строка или еще что-то - это автоматически указывает, что нужно сделать. Если такого инструмента нет, как бы вы его написали (например, распространяя ReSharper с нуля, используя библиотеки)?

+0

Вы можете сделать это с помощью шаблона [T4.] (Http://msdn.microsoft.com/en-us/library/bb126445.aspx) У меня нет образца, который удобен, но я мог бы попробовать написать один, если вам интересно. Я мог видеть, как это может быть действительно полезно при разработке Test-First (TDD); трюк заключается в том, что ваш код компилируется во время написания тестов для методов, которые еще не существуют. Другая проблема, конечно, в том, что шаблон T4, вероятно, должен быть более умным, чем то, что вы описали, и я сомневаюсь, что вы предоставили полную спецификацию в своем примере. – 2012-04-03 20:42:30

+0

@RobertHarvey, я обсуждал это с коллегой, и у него создается впечатление, что может быть слишком много выводов. Я был бы просто доволен тем, что работает значительно умнее, чем текущее безумие «правого клика» :-) – Sklivvz 2012-04-03 20:53:04

ответ

4

Что вы, кажется, нужно это анализатор для вашего языка (Java), а также распознаватель имен и типов. («Конструктор таблиц символов»).

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

Обычно распознаватель имен/типов жалуется, когда он не может найти определение. То, что вы хотите, это найти «неопределенную» вещь, которая вызывает проблему, и вывести для нее тип.

Для

IPerson p = new Person(); 

имя распознавателя знает, что "лицо" и "IPerson" не определены. Если бы это было

Foo p = new Bar(); 

не было бы никакой подсказки, что вы хотели интерфейс, только что Foo является своим родом абстрактного родителя Бара (например, класс или интерфейс). Поэтому решение, которое должно быть известно этому инструменту («всякий раз, когда вы найдете такую ​​конструкцию, предположим, что Foo - это интерфейс ...»). Вы можете использовать эвристику: IFoo и Foo означает, что IFoo должен быть интерфейсом, и где-то кто-то должен определить Foo как класс, реализующий этот интерфейс. После того, как инструмент принял это решение, то ему необходимо будет обновить свои таблицы символов, так что он может перейти на другие заявления:

Для

p.Name = "Sklivvz"; 

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

При том, что утверждение:

Assert.AreEqual("Sklivvz", p.Name); 

имена и типы разрешиться без дополнительного выпуска.

Содержание объектов IFoo и Foo является своего рода достоянием; вам не пришлось использовать get и set, но это личный вкус.

Это не будет работать так хорошо, когда у вас есть несколько объектов в том же заявлении:

x = p.a + p.b ; 

Мы знаем и б вероятные поля, но вы не можете догадаться, что числовой тип, если на самом деле они являются numeric, или если они являются строками (это законно для строк в Java, dunno о C#). Для C++ вы даже не знаете, что означает «+»; это может быть оператор класса Bar. Итак, вам нужно собрать ограничения, например, «a - некоторое неопределенное число или строка» и т. Д., А поскольку инструмент собирает доказательства, он сужает набор возможных ограничений. (Это работает так, как эти проблемы с текстом: «У Джо семь сыновей. Джефф выше Сэма, Гарри не может спрятаться за Сэмом ... который является близнецом Джеффа?», Где вам нужно собрать доказательства и снять невозможность). Вам также нужно беспокоиться о том, в каком случае вы столкнетесь с противоречием.

Вы можете исключить случай p.a + p.b, но тогда вы не можете писать свои юнит-тесты безнаказанно. Там есть стандартные ограничители, если вы хотите безнаказанности. (Какая концепция).

ОК, у нас есть идеи, теперь это можно сделать на практике?

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

Возможно, наш DMS Software Reengineering Toolkit с его Java Front End может это сделать. DMS - инструмент инструментального инструментария для людей, которые хотят создавать инструменты, которые обрабатывают компьютерные языки произвольными способами. (Подумайте о «вычислениях с фрагментами программы, а не цифрами»).

DMS предоставляет универсальные синтаксические машины и может строить дерево для любого внешнего интерфейса (например, Java, а также переднего конца C#). Причина, по которой я выбрал Java, заключается в том, что на нашем интерфейсе Java есть все, что называется машиной разрешения имен и типов, и она предоставляется в исходной форме, поэтому ее можно согнуть. Если вы столкнулись с тривиальным решателем ограничений, вы, вероятно, можете сгибать распознаватель имен Java, чтобы выяснить типы. DMS позволит вам собирать деревья, соответствующие фрагментам кода, и объединять их в более крупные; поскольку ваш инструмент собирал факты для таблицы символов, он мог бы построить примитивные деревья.

Где-то вы должны решить, что все готово. Сколько единичных тестов инструмент должен видеть , прежде чем он узнает весь интерфейс? (Думаю, он ест все, что вы предоставляете?). После завершения сборки он собирает фрагменты для различных элементов и создает AST для интерфейса; DMS может использовать свой симпатичный принтер для преобразования этого АСТ обратно в исходный код, как вы показали.

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

Но идея прекрасно работает в принципе с использованием DMS.

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

Вам все еще нужно было сгенерировать фрагменты кода и собрать их. Вы можете попытаться сделать это путем хакерства строк; мой (длинный) опыт с склеиванием программных битов заключается в том, что, если вы делаете это со строками, вы в конечном итоге создадите беспорядок. Вам действительно нужны куски, которые представляют фрагменты кода известного типа, которые можно комбинировать только способами, которыми позволяет грамматика; DMS делает это, таким образом, беспорядок.

-2

Я нахожу, что всякий раз, когда мне нужен инструмент генерации кода, подобный этому, я, вероятно, пишу код, который можно сделать немного более общим, поэтому мне нужно только написать его один раз. В вашем примере эти геттеры и сеттеры, похоже, не добавляют никакой ценности для кода - на самом деле, это действительно просто утверждение о том, что механизм getter/setter в C# работает.

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

Кстати, вы можете взглянуть на NBehave?

1

Если вы планируете написать свою собственную реализацию, я бы определенно предложил вам взглянуть на двигатели шаблонов NVelocity (C#) или Velocity (Java).

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

-2

Я использую Rhino Mocks для этого, когда мне просто нужен простой заглушка.

http://www.ayende.com/wiki/Rhino+Mocks+-+Stubs.ashx

+0

Вот как я это делаю, говорит так много кода и времени! – 2008-09-18 11:21:11

+0

Он не генерирует файлы ... Я хочу, чтобы заглушки были созданы, поэтому я могу их вручную изменить. – Sklivvz 2008-09-18 12:23:58

-1

Visual Studio поставляется с некоторыми функциями, которые могут быть полезны для вас здесь:

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

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

  • Ctrl-. (открыть смарт-тег)
  • ENTER (для создания заглушки)
  • F12 (перейти к определению, принять вас на новый метод)

смарт-тег появляется только если IDE считает, что не существует метода, который соответствует. Если вы хотите создать, когда смарт-тег не вставлен, вы можете перейти к Edit-> Intellisense-> Generate Method Stub.

Фрагменты. Небольшие шаблоны кода, которые позволяют легко генерировать бит общего кода. Некоторые из них просты (попробуйте «если [TAB] [TAB]»).Некоторые из них сложны («switch» будет генерировать случаи для перечисления). Вы также можете написать свой собственный. Для вашего случая попробуйте «class» и «prop».

См. Также «How to change “Generate Method Stub” to throw NotImplementedException in VS?» для информационных фрагментов в контексте GMS.

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

public string Name { get; set; } 

создать класс. В обозревателе решений RClick в имени проекта или в подпапке выберите Add-> Class. Введите имя нового класса. Hit ENTER. Вы получите объявление класса в правом пространстве имен и т. Д.

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

Это не совсем автоматическое решение на 100%, которое вы ищете, но я думаю, что это хорошее смягчение.

0

Мне нравится CodeRush от DevExpress. У них огромный настраиваемый механизм шаблонов. И лучшее для меня - это не диалоговые окна. Они также имеют функциональность для создания методов и интерфейсов и классов из интерфейса, который не существует.

3

Удивительно, как никто ничего не дал по тому, что вы просили.

Я не знаю ответа, но я расскажу об этом.

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

Теперь я ни в коем случае не знаю, как это сделать, поскольку я никогда не строил ничего для resharper, но это то, что я постараюсь сделать. Логично, что это можно сделать.

И если вы напишете какой-нибудь код, ПОЖАЛУЙСТА, опубликуйте его, поскольку я мог бы найти это полезное, а также возможность генерировать весь скелет за один шаг. Очень полезно.

1

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

0

Попробуйте посмотреть на Pex, А майкрософт проекта модульного тестирования, который до сих пор проводятся исследования

research.microsoft.com/en-us/projects/Pex/

0

Я думаю, что вы ищете набор инструментов для fuzzing (https://en.wikipedia.org/wiki/Fuzz_testing).

Al жестким Я никогда не использовал, вы могли бы дать Randoop.NET возможность генерировать «юнит-тесты» http://randoop.codeplex.com/

1

Я думаю, что реальное решение этой проблемы было бы очень специализированный анализатор. Поскольку это не так просто сделать, у меня есть более дешевая идея. К сожалению, вам придется изменить способ писать тесты (а именно, только создание объекта):

dynamic p = someFactory.Create("MyNamespace.Person"); 
p.Name = "Sklivvz"; 
Assert.AreEqual("Sklivvz", p.Name); 

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

Некоторые недостатки, которые я вижу:

  • нужно запустить код в «двух» режимах, которая раздражает.
  • Для того чтобы прокси-сервер мог «видеть» и записывать вызовы, они должны быть выполнены; поэтому, например, должен выполняться код в блоке catch.
  • Вы должны изменить способ создания объекта под тестом.
  • Вы должны использовать dynamic; вы потеряете безопасность во время компиляции в последующих прогонах, и у нее будет удар производительности.

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

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