Создайте новую папку и добавьте следующие папки:
- строить
- сборка\задачи
- Особенности
- страницы-объекты
- отчеты
- шаги
- служба поддержки
Создайте файл пакета по умолчанию, выполнив:
npm init -y
Установите все необходимые пакеты:
npm i chai cucumber cucumber-tsflow gulp gulp-clean gulp-protractor gulp-protractor-cucumber-html-report gulp-typescript protractor protractor-cucumber-framework require-dir typings --save-dev
Это установит следующие пакеты npm:
Настройка глотка
В корневой папке создайте файл с именем gulpfile.ts
и добавьте следующее содержимое:
// all gulp tasks are located in the ./build/tasks directory // gulp configuration is in files in ./build directory require('require-dir')('build/tasks');
Настройка машинописного транспиля
Создайте файл build.js
в каталоге build\tasks
и добавьте следующее содержимое:
var gulp = require("gulp"); var ts = require("gulp-typescript"); var tsProject = ts.createProject('tsconfig.json'); var clean = require("gulp-clean"); var paths = require('../paths'); gulp.task("clean", function () { return gulp.src(paths.dist, { read: false }).pipe(clean()); }); gulp.task("build", ["clean"], function () { var tsResult = tsProject.src().pipe(ts(tsProject)); return tsResult.js.pipe(gulp.dest(paths.dist)); });
В папку build
добавьте файл с именем paths.js
и добавьте следующее содержимое:
module.exports = { dist: "dist/" };
Это будет место, где вы будете хранить все пути к ресурсам, используемым в ваших сценариях сборки. Затем добавьте параметры конфигурации для Typescript, добавив файл с именем tsconfig.json
в корневую папку вашего проекта со следующим содержимым:
{ "compilerOptions": { "moduleResolution": "node", "module": "umd", "target": "es5", "declaration": true, "sourceMap": true, "removeComments": false, "experimentalDecorators": true }, "exclude": [ "node_modules" ], "filesGlob": [ "steps/**/*.ts" ] }
Добавьте определения типизации для библиотек javascript, которые мы используем:
typings i dt~chai dt~angular-protractor dt~selenium-webdriver --save --global
Настройка транспортира
В папку build
добавьте файл с именем test.js
и добавьте следующее содержимое:
var gulp = require("gulp"); var webdriver_update = require("gulp-protractor").webdriver_update; var protractor = require("gulp-protractor").protractor; var reporter = require("gulp-protractor-cucumber-html-report"); var paths = require('../paths'); gulp.task("webdriver_update", webdriver_update); gulp.task("e2e", ["build"], function () { return gulp.src(paths.features) .pipe(protractor({ configFile: "protractor.conf.js" })) .on("error", function (e) { throw e; }); }); gulp.task("e2e-report", function () { gulp.src(paths.testResultJson) .pipe(reporter({ dest: paths.e2eReports })); });
Это позволит вам использовать gulp для запуска теста транспортира. Расширьте свой файл paths.js
, включив в него новые пути, использованные выше:
module.exports = { dist: "dist/", features: "features/**/*.feature", testResultJson: "./reports/cucumber-test-results.json", e2eReports: "reports/e2e" };
В корневую папку вашего проекта добавьте файл protractor.conf.js
и добавьте следующее содержимое:
var paths = require('build/paths'); exports.config = { directConnect: true, multiCapabilities: [ {'browserName': 'firefox'}, {'browserName': 'chrome'}, {'browserName': 'internet explorer'} ] seleniumServerJar: './node_modules/gulp-protractor/node_modules/protractor/selenium/selenium-server-standalone-2.53.1.jar', framework: 'custom', frameworkPath: 'node_modules/protractor-cucumber-framework', specs: [ paths.features ], jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 30000 }, cucumberOpts: { require: [paths.distFiles, paths.support], format: "json" } };
Вышеупомянутая установка будет запускать ваши сквозные тесты в Chrome, Firefox и Internet Explorer 11 параллельно. Вы можете удалить браузеры, если, например, вы можете тестировать свои тесты только в Chrome.
Вы также можете заменить свойство multiCapabilities
текстом ниже, чтобы тестировать только в Chrome.
capabilities: { 'browserName': 'chrome' },
Расширьте файл build\paths.js
еще раз, чтобы включить новые пути:
module.exports = { dist: "dist/", distFiles: "dist/**/*.js", testResultJson: "./reports/cucumber-test-results.json", e2eReports: "reports/e2e" features: "features/**/*.feature", support: "support/*.js" };
Настройка создания скриншота при сбое
Было бы неплохо увидеть скриншот текущего представления, если тест не пройден. Сценарий ниже добавит скриншот к неудачному шагу.
Создайте новый файл с именем TakeScreenshot.js
в папке support
со следующим содержимым:
module.exports = function TakeScreenshot() { this.After(function (scenario, callback) { if (scenario.isFailed()) { browser.takeScreenshot().then(function (png) { var decodedImage = new Buffer(png.replace(/^data:image\/(png|gif|jpeg);base64,/,''), 'base64'); scenario.attach(decodedImage, 'image/png'); callback(); }); } else { callback(); } }); };
Добавьте еще один новый файл с именем jsonOutputHook.js
(тоже в папку support
) и добавьте содержимое:
var paths = require("../build/paths"); module.exports = function JsonOutputHook() { var Cucumber = require('cucumber'); var JsonFormatter = Cucumber.Listener.JsonFormatter(); var fs = require('fs'); var path = require('path'); JsonFormatter.log = function (json) { fs.writeFile(path.join(__dirname, '../'+ paths.testResultJson), json, function (err) { if (err) throw err; console.log('json file location: ' + path.join(__dirname, '../' + paths.testResultJson)); }); }; this.registerListener(JsonFormatter); };
Когда это будет сделано, gulp e2e
проведет ваш сквозной тест, а gulp e2e-report
создаст отчет на основе результатов последнего gulp e2e
запуска.
Пример неудачного сценария
Пример функции
Функция определяется в файле .feature
и может иметь несколько сценариев, определенных на языке Gherkin. Огурец — это язык, позволяющий сохранить определение бизнес-логики, которым можно поделиться с заинтересованными сторонами проекта.
Файл .feature
(хранящийся в папке features
) может, например, содержать следующий сценарий:
Feature: Create a new teamie In order to start a new teamie As an team member that is the organizer of a teamie survey for my team I want to be able to create a teamie so that my team can improve Background: Given I received an link to create a new Teamie And I opened the link in my browser Scenario: Team name must be set Given I don't enter a team name for my team Then A error message should occur 'The team name cannot be empty'
Чтобы реализовать логику вышеописанной истории, мы реализуем определение шага (в папке steps
) и объекты страницы (в папке page-objects
).
Объект страницы — это многократно используемая структура самой страницы. Это позволяет нам написать эту логику один раз и использовать ее в любом определении шага, которое требует этого. Это означает, что объект страницы — это объект, который содержит весь наш Selenium-подобный синтаксис для доступа к объектам на нашей странице.
export default class NewTeamiePageObject { public static setTeamName(name: string): webdriver.promise.Promise<void> { return element(by.id("lblName")).sendKeys(name); } }
Затем у нас есть файл шагов, в этом файле будут методы, которые будут вызываться CucumberJS, если они соответствуют шаблону регулярного выражения, определенному в декораторе. И, в свою очередь, вызовет методы, определенные в нашем объекте страницы.
Ниже приведен пример кода для обработки шага Given I don't enter a team name for my team
. Каждый класс, содержащий шаги, должен быть украшен декоратором @bindings
, а каждый метод должен быть украшен декоратором @given
(или @when
, @then
).
CucumberJS проверит все регулярные выражения, определенные в определениях шагов, и выполнит определение шага, если оно совпадает с текстом шага.
Как вы можете видеть ниже, есть также метод с регулярным выражением /^I enter the team name '(.*)' for my team$/
. Это регулярное выражение будет обрабатывать строки, соответствующие I enter the team name 'team 1' for my team
, и передавать значение team 1
методу, чтобы сделать ваш код более гибким.
import { binding, given, then, when } from "cucumber-tsflow"; import { expect } from "chai"; import { newTeamiePageObject } from "./../../page-objects/NewTeamiePageObject"; @binding() class NewTeamieSteps { @given(/^I don't enter a team name for my team$/) public GivenIDontEnterATeamNameForMyTeam (callback): void { newTeamiePageObject.setTeamName("").then(callback); } @given(/^I enter the team name '(.*)' for my team$/) public GivenEnterATeamNameForMyTeam (teamName, callback): void { newTeamiePageObject.setTeamName(teamName).then(callback); } } export = NewTeamieSteps;
Примечание: последняя строка (экспорт) должна быть написана таким образом, чтобы все заработало, использование export default class
не сработает :-(
Первоначально опубликовано на www.eriklieben.com 23 августа 2016 г.