Подтвердить что ты не робот

Создание классов автоматически из модульных тестов?

Я ищу инструмент, который может принимать unit test, например

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 с нуля, используя библиотеки)?

4b9b3361

Ответ 1

То, что вам кажется необходимым, - это парсер для вашего языка (Java), а также имя и тип распознавателя. ( "Конструктор таблиц символов" ).

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

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

Для

 IPerson p = new Person();

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

 Foo  p =  new Bar();

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

Для

 p.Name = "Sklivvz";

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

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

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

имена и типы разрешаются без дальнейших проблем.

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

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

 x = p.a + p.b ;

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

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

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

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

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

См. также "Как изменить "Создать метод Stub" , чтобы вывести NotImplementedException в VS?" для информационных фрагментов в контексте GMS.

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

public string Name { get; set; }

создать класс. В обозревателе решений RClick в имени проекта или вложенной папке выберите Добавить- > Класс. Введите имя нового класса. Нажмите ВВОД. Вы получите объявление класса в правом пространстве имен и т.д.

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

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

Ответ 10

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

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

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