После двух выпусков версии с открытым исходным кодом protobuf-dt и десяти внутренних для Google сложность кода возрастает до такой степени, что требуется более всестороннее тестирование. Несмотря на то, что практики, описанные в этом посте, можно использовать для тестирования различных аспектов редактора Eclipse, мы сосредоточимся на тестировании области видимости .
Проверка правильности определения объема работ включает в себя:
- Задание и анализ содержимого буфера для протокола, используемого для тестирования.
- Найти определенный элемент в разобранном файле
- Убедитесь, что область действия возвращает все возможные элементы, которые могут соответствовать элементу, найденному на шаге № 2
Дополнительное требование заключается в том, чтобы сделать тесты как можно более легкими (например, не нужно создавать экземпляр Eclipse для запуска тестов).
1. Определение и анализ содержимого буфера протокола для использования
Моей первой попыткой было поместить весь код буфера протокола в StringBuilder, а затем передать его в мой XtextRule, чтобы вернуть AST, следующим образом:
@Rule public XtextRule xtext = createWith(integrationTestSetup()); @Test public void should_provide_Property_fields_for_custom_field_option() { StringBuilder proto = new StringBuilder(); proto.append("package com.google.proto; ") .append("import 'google/protobuf/descriptor.proto'; ") .append(" ") .append("extend google.protobuf.FieldOptions { ") .append(" optional int32 code = 1000; ") .append(" optional int32 info = 1001; ") .append("} ") .append(" ") .append("message Person { ") .append(" optional boolean active = 1 [(code) = 68];") .append("} "); Protobuf root = xtext.parseText(proto); // test implementation }
Излишне говорить, что написание и поддержание этого — настоящая PITA. Чтобы сделать его читаемым, мне нужно добавить дополнительные пробелы в каждой строке, чтобы все выглядело квадратно Кроме того, для копирования / вставки кода из редактора буфера протокола необходимо также добавить к каждой строке добавление и кавычки, а также форматирование. Кроме того, это делает метод тестирования слишком длинным.
Альтернативным подходом было наличие файлов .proto в файловой системе. Я не был доволен этим подходом, так как любой, кто читает код, должен будет пойти в два места, чтобы понять, что делает тест.
Наконец, я нашел лучший подход: укажите текст буфера протокола в качестве комментария!
Я сам не придумал эту идею, я просто позаимствовал ее у CDT . После реализации более простого, но похожего подхода мой тест выглядит следующим образом:
// package com.google.proto; // import 'google/protobuf/descriptor.proto'; // // extend google.protobuf.FieldOptions { // optional int32 code = 1000; // optional int32 info = 1001; // } // // message Person { // optional boolean active = 1 [(code) = 68]; // } @Test public void should_provide_Property_fields_for_custom_field_option() { Protobuf root = xtext.root(); // test implementation }
Это большое улучшение! Во-первых, метод тестирования оказывается короче и легче для чтения. Во-вторых, легко набрать что-нибудь в редакторе буфера протокола, скопировать его и вставить поверх метода тестирования Затем я просто должен выделить его и нажать Ctrl + /, чтобы преобразовать текст в комментарий.
В этом новом сценарии мой пользовательский XtextRule выполняет следующие действия:
- извлекает комментарий каждого метода тестирования и создает имя метода> отображение комментариев
- анализирует комментарий тестового метода и создает AST непосредственно перед выполнением тестового метода, а не ранее
- сохраняет ссылку на корневой узел AST, который будет использоваться самим тестовым методом
Это выглядит хорошо, но как насчет импортированных файлов .proto?
При тестировании области видимости абсолютно необходимо убедиться, что импортированные типы включены правильно.
Первоначально у меня были файлы .proto, которые нужно было импортировать в файловую систему и хранить в Git, что ужасно по той причине, о которой я упоминал ранее.
Чтобы улучшить ситуацию, я позаимствовал еще одну идею из CDT: укажите текст и имя файла .proto, который будет импортирован в комментариях, XtextRule создаст файл в файловой системе непосредственно перед выполнением тестового метода.
Вот пример:
// // Create file custom-options.proto // // package com.google.proto; // // import "google/protobuf/descriptor.proto"; // // extend google.protobuf.FileOptions { // optional int32 code = 1000; // optional int32 info = 1002; // } // package com.google.proto; // // import 'custom-options.proto'; // // option (code) = 68; @Test public void should_provide_imported_Property_fields_for_custom_option() { // test implementation }
В приведенном выше примере у нас есть два комментария для метода тестирования. Первый говорит моему XtextRule создать файл с именем «custom-options.proto» перед выполнением метода теста. Второй комментарий будет проанализирован и чей AST будет сохранен XtextRule (как в предыдущем примере).
Аккуратно, не правда ли? ?
2. Найти конкретный элемент в разобранном файле
Этот шаг необходим, чтобы гарантировать, что область действия возвращает все возможные типы, на которые может указывать данная ссылка.
В этом примере:
// package com.google.proto; // import 'google/protobuf/descriptor.proto'; // // extend google.protobuf.FieldOptions { // optional int32 code = 1000; // optional int32 info = 1001; // } // // message Person { // optional boolean active = 1 [(info) = 68]; // } @Test public void should_provide_Property_fields_for_custom_field_option() { }
Мой тест будет проверять, что область видимости возвращает код параметров поля и информацию как потенциальные совпадения (код). Для этого мне сначала нужно найти соответствующий элемент AST, который (код) представляет.
Допустим, я хочу найти информацию о параметрах настраиваемого поля. Мой оригинальный подход потребовал бы следующего:
- Найти все элементы в AST типа CustomFieldOption
- Если какой-либо результат возвращается из # 1, найдите тот, чье имя «info»
Вот пример:
Protobuf root = xtext.root(); // statically imported from CustomFieldOptionFinder CustomFieldOption option = findCustomFieldOption(name("info"), in(root));
Это решение само по себе не так уж плохо, и оно работает! К сожалению, в AST требуется один специализированный искатель для каждого типа. Для относительно простого языка, такого как Protocol Buffers, потребовалось бы более 10 искателей, что является слишком большим количеством кода для обслуживания только для тестирования.
Введите CDT с лучшим подходом, который требует только один искатель для всех типов в AST. Этот искатель ищет элементы следующим образом:
- Найти первый элемент, соответствующий тексту
- Убедитесь, что найденный элемент относится к указанному типу и имеет указанное имя
Например:
CustomFieldOption option = xtext.find("info", ")", CustomFieldOption.class);
Чтобы найти пользовательский параметр «информация», этот искатель будет:
- объединяет строки, переданные в качестве аргументов, и находит первый элемент, соответствующий тексту «информация»
- убедитесь, что найденный элемент является CustomFieldOption и имеет имя «info» (только первый аргумент String)
Как видите, этот подход является полной противоположностью моему первоначальному.
Поскольку нужен только один искатель, я могу сделать так, чтобы мой XtextRule создал его и передал ему корень AST. Таким образом, мне не нужно передавать его поисковику каждый раз, когда я выполняю поиск.
3. Проверка того, что область видимости возвращает правильные типы
На самом деле это зависит от DSL. Моя единственная рекомендация: если вы используете JUnit, пишите свои собственные средства сравнения Hamcrest, чтобы тестовый код был коротким и читаемым, без дублирования.
Собираем все вместе
Вот как выглядит один из моих тестов для protobuf-DT:
// message Person { // optional Type type = 1 [ctype = STRING]; // } @Test public void should_provide_Property_fields_for_native_field_option() { // We have an overloaded version of "find" that takes only one <code>String</code>, // to be used when the text to find and the name to match are the same. NativeFieldOption option = xtext.find("ctype", NativeFieldOption.class); IScope scope = provider.scope_PropertyRef_property(option.getProperty(), reference); Collection<Property> fieldOptions = descriptor().optionsOfType(FIELD); assertThat(descriptionsIn(scope), containAll(fieldOptions)); }
Пожалуйста, не стесняйтесь нажимать эти ссылки, чтобы найти код:
- XtextRule
- ModelFinder
- или просто получите исходный код проекта, как описано здесь
Обратная связь всегда приветствуется! ?
Будущие посты
Есть несколько полезных вещей, которые я сделал с Xtext, о которых я хотел бы рассказать в ближайшем будущем:
- Добавление проверки орфографии в комментарии и строки
- Открытие файлов вне рабочей области Eclipse
- Заставить Xtext работать с именами файлов, а не с расширениями
Теперь дело за временем и энергией!