1

我的问题的简短版本

在为 Angular 单页 Web 应用程序编写的 Cucumber 测试中,我如何完成通常在场景的“给定”部分执行的任务(例如设置测试数据、定义数据库记录关联以及确保测试之间的干净数据库状态) 在测试全栈时,前端应用程序及其后端?应用程序源代码存储在两个独立的 Git 存储库中,一个用于前端应用程序,一个用于后端。后端是使用 Rails API gem 用 Ruby 编写的。全栈测试这个应用程序的挑战来自于它实际上是两个应用程序这一事实,而更传统的 Ruby on Rails 应用程序不是作为单页应用程序实现的。

我的问题的完整版

我正在寻找为 Web 应用程序编写一系列 Cucumber 测试。该应用程序由一个用 Angular 编写的前端单页应用程序和一个使用 Rails API 编写的后端 API 组成。前端的源代码和后端的源代码都位于各自的 Git 存储库中,从而在两个代码库之间提供了清晰的分离。此外,该应用程序使用 MySQL 和 Elasticsearch。

我过去在以前的 Ruby on Rails 项目中使用过 Cucumber。这些项目不是作为单页应用程序开发的。在这些项目中,很容易在 Cucumber 步骤定义中创建 Ruby 对象作为测试数据。例如,考虑以下不是单页应用程序的 Rails 项目中的功能文件:

# features/home_page.feature
Feature: Home page

  Scenario: Viewing application's home page
    Given there's a post titled "My first" with "Hello, BDD world!" content
    When I am on the homepage
    Then I should see the "My first" post

此功能文件中的步骤可以通过以下步骤定义来实现:

# features/step_definitions/home_page_steps.rb
Given(/^there's a post titled "(.*?)" with "(.*?)" content$/) do |title, content|
  @post = FactoryGirl.create(:post, title: title, content: content)
end

When(/^I am on the homepage$/) do
  visit root_path
end

Then(/^I should see the "(.*?)" post$/) do |title|
  @post = Post.find_by_title(title)

  page.should have_content(@post.title)summary of the question
  page.should have_content(@post.content)
end

在不是作为单页应用程序开发的 Ruby on Rails 项目中,测试工具可以作为 Ruby gems 包含在项目中。对我来说,这些工具包括:

group :test do
  gem 'shoulda-matchers'
  gem 'cucumber-rails', require: false
  gem 'database_cleaner'
  gem 'selenium-webdriver'
end

group :development, :test do
  gem 'factory_girl_rails'
end

如您所见,这包括 Factory Girl,用于将 Ruby 对象设置为测试数据并定义数据库记录关联,以及 Database Cleaner,用于确保测试之间的干净数据库状态。使用 JavaScript 的 Cucumber 场景需要包含 Selenium WebDriver。

在我的单页应用程序中情况有所不同。如上所述,应用程序分为两个独立的代码库,一个用于 Angular 前端单页应用程序,另一个用于 Rails API 后端接口。

但是,像我的项目这样的单页应用程序仍然具有与更传统的 Rails 应用程序相同的测试要求,这些应用程序不是作为单页应用程序构建的。有必要对应用程序进行全栈测试,以确保前端和后端的每个组件都能按预期协同工作。需要定义 Cucumber 步骤,在测试之前创建“给定”先决条件,并且有必要确保测试之间的数据库干净。

如何使用 Cucumber 测试这样的应用程序,一个有两个代码库,实现为单页应用程序?有一个可用于 JavaScript 的 Cucumber 版本,称为 CucumberJS。但是,我不知道如何使用 CucumberJS 创建固定装置、记录关联以及确保测试之间的数据库干净。还有一个用于测试用 Angular 编写的 JavaScript 的工具,称为 Protractor。我想这个工具会取代 Selenium WebDriver。

4

1 回答 1

0

回答简短版本的问题。

全栈测试这个应用程序的挑战来自于它实际上是两个应用程序

您所描述的对于单页应用程序来说是典型的。我有许多 spa 示例(包含 E2E),其中后端是 PHP、Dot.net 或云 Web 服务(parse.com)中的 RESTful API。在我看来,最好将此类应用程序视为 2 个不同的应用程序,并为每个应用程序编写 2 组不同的测试。我相信这是编写稳定、可扩展和快速的 e2e 测试的唯一方法。

基本上我们所做的是创建一个本地非持久模拟 api(在 javascript 中),特别是用于测试。以下是此类 api 的要求/指南列表:

  1. 为测试套件提供模拟数据(读取 - 它与测试套件耦合)
  2. 涵盖了足以使测试通过的方法。
  3. 它是非持久的(请求参数就是做出响应的全部,尽管可能有例外)。所有持久性都在客户端(本地存储、cookies)上。这很重要,因为只需清除客户端存储并刷新页面 - 您就会获得应用程序的全新状态!在哪里测试单体——你必须清除并重新填充数据库,这是浪费时间并且完全不可扩展。
  4. 它嵌入到 Angular 存储库中,是测试套件和开发过程的一部分。

在实践中看起来如何。假设我们正在测试网站的登录功能,它有 3 个场景:

var homepage = require('pages/homepage'); //page object for home page

describe('Authentication', function () {

  beforeEach(function () {
    browser.manage().deleteAllCookies(); //clear persistance
    browser.refresh(); //refresh page
    homepage.get(); //go to homepage
  });

  it('should show error when user enters wrong credentials', function () {
    homepage.signIn('admin', '123'); //submit invalid credentials 
    expect(browser.getCurrentUrl()).toMatch('/home$');    
    expect(homepage.getSignInForm()).toContain('wrong credentials');
  });

});

为了满足这些测试,我们所要做的就是为 POST /login 请求实现一个 api:

router.get('/login', function (req, res) {
  var username = req.body.username, password = req.body.password;

  if (password !== 'admin') {
    res.status(404).json({"error": "wrong credentials"});
  } else {
    res.json(User(username}));
  }
});
于 2015-06-15T12:18:58.080 回答