简体   繁体   English

设置测试数据以对单页Web应用程序及其后端进行全栈测试

[英]Setting up test data for full-stack testing of a single page web application and its back-end

Short version of my question: 我的问题的简短版本

In Cucumber tests written for an Angular single page web application, how do I accomplish the tasks usually performed in the "given" section of a scenario (such as setting up test data, defining database record associations, and ensuring a clean database state between tests) when testing the full-stack, both the front-end application and its back-end? 在为Angular单页Web应用程序编写的Cucumber测试中,我如何完成通常在场景的“给定”部分中执行的任务(例如设置测试数据,定义数据库记录关联以及确保测试之间的数据库状态干净) )在测试全栈时,前端应用程序及其后端? The application source code is stored in two separate Git repositories, one for the front-end application and one for the back-end. 应用程序源代码存储在两个单独的Git存储库中,一个用于前端应用程序,一个用于后端。 The back-end is written in Ruby using the Rails API gem. 后端是使用Rails API gem用Ruby编写的。 The challenge in testing this application full-stack comes from the fact that it's actually two applications, in contrast with a more traditional Ruby on Rails application not implemented as a single page application. 全栈测试该应用程序所面临的挑战来自这样一个事实,即它实际上是两个应用程序,而不是传统的Ruby on Rails应用程序未实现为单页应用程序。

Full version of my question: 我的问题的完整版本

I'm looking to write a series of Cucumber tests for a web application. 我正在为Web应用程序编写一系列的Cucumber测试。 The application consists of a front-end single page application written in Angular, and a back-end API written using Rails API. 该应用程序由用Angular编写的前端单页应用程序和使用Rails API编写的后端API组成。 The source code for the front-end and the source code for the back-end each reside in their own Git repositories, providing a clean separation between the two codebases. 前端的源代码和后端的源代码分别位于各自的Git存储库中,从而在两个代码库之间提供了清晰的分隔。 Furthermore, the application uses MySQL and Elasticsearch. 此外,该应用程序使用MySQL和Elasticsearch。

I've used Cucumber in the past on previous Ruby on Rails projects. 过去,我在先前的Ruby on Rails项目中使用过Cucumber。 These projects were not developed as single page applications. 这些项目不是作为单页应用程序开发的。 In these projects it was easy to create Ruby objects as test data in Cucumber step definitions. 在这些项目中,很容易在cumberber步骤定义中创建Ruby对象作为测试数据。 For example, consider the following feature file from a Rails project that was not a single page application: 例如,考虑不是单个页面应用程序的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

The steps in this feature file can be implemented with the following step definitions: 可以使用以下步骤定义来实现此功能文件中的步骤:

# 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

In Ruby on Rails projects that were not developed as single page applications, testing tools can be included into the project as Ruby gems. 在不是作为单页应用程序开发的Ruby on Rails项目中,测试工具可以作为Ruby gems包含在项目中。 For me, these tools would include: 对我来说,这些工具包括:

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

As you can see, this includes Factory Girl, used for setting up Ruby objects as test data and defining database record associations, and Database Cleaner, used to ensure a clean database state between tests. 如您所见,这包括用于将Ruby对象设置为测试数据并定义数据库记录关联的Factory Girl,以及用于确保测试之间的干净数据库状态的Database Cleaner。 The inclusion of Selenium WebDriver is required for Cucumber scenarios which use JavaScript. 使用JavaScript的Cucumber方案要求包含Selenium WebDriver。

The situation is different in the case of my single page application. 对于我的单页应用程序,情况有所不同。 As described above, the application is broken into two separate code bases, one for the Angular front-end single page application and the other for the Rails API back-end interface. 如上所述,该应用程序分为两个单独的代码库,一个用于Angular前端单页应用程序,另一个用于Rails API后端接口。

However, single page applications such as my project still have the same testing requirements as more traditional Rails applications not built as single page applications. 但是,像我的项目这样的单页应用程序仍然具有与不作为单页应用程序构建的更传统的Rails应用程序相同的测试要求。 It's necessary to test the application full-stack to ensure each component, both front and backend, work together as expected. 有必要对应用程序进行全栈测试,以确保前端和后端的每个组件都能按预期工作。 There will need to be Cucumber steps defined which create the "given" preconditions prior to a test, and it will be necessary to ensure a clean database between tests. 在测试之前,需要定义黄瓜步骤以创建“给定的”前提,并且有必要确保测试之间的数据库干净。

How do I test with Cucumber an application such as this, one with two code bases, implemented as a single page application? 如何使用Cucumber测试这样的应用程序(一个具有两个代码库的应用程序)实现为单页应用程序? There's a version of Cucumber available for JavaScript called CucumberJS. 有一个适用于JavaScript的Cucumber版本,称为CucumberJS。 However, I don't know how to create fixtures, record associations, and to ensure a clean database between tests using CucumberJS. 但是,我不知道如何使用CucumberJS创建固定装置,记录关联以及如何确保测试之间的干净数据库。 There's also a tool for testing JavaScript written with Angular called Protractor. 还有一个用于测试用Angular编写的JavaScript的工具,称为Protractor。 I imagine this tool taking the place of Selenium WebDriver. 我想象该工具将取代Selenium WebDriver。

Answer to the short version question. 回答简短版本问题。

The challenge in testing this application full-stack comes from the fact that it's actually two applications 全栈测试该应用程序的挑战来自以下事实:它实际上是两个应用程序

What you describe is tipical to single page apps. 您所描述的内容对单页应用程序来说是典型的。 I have a number of spa examples (covered with E2E) where the backend is a RESTful API in PHP, Dot.net or ever a cloud web-service (parse.com). 我有很多spa示例(用E2E覆盖),其中后端是PHP,Dot.net或云Web服务(parse.com)中的RESTful API。 To my point of view, it is best to treat such apps, to qoute you agin, as 2 different apps and write 2 different sets of test for each. 以我的观点,最好将此类应用程序视为2个不同的应用程序,并为每个应用程序编写2套不同的测试集。 I believe that is the only way to write stable, scalable and fast e2e tests. 我相信这是编写稳定,可扩展和快速的e2e测试的唯一方法。

Basically what we do is create a local non-persistent mock api (in javascript) especially for testing. 基本上,我们要做的是创建一个本地的非持久性模拟API(使用javascript),专门用于测试。 Here's a list of requirements/guidelines for such an api: 以下是此类api的要求/指南列表:

  1. Provides mock data for the test suit (read - it is coupled with test suit) 提供测试服的模拟数据(已读取-结合测试服)
  2. Covers just enough methods to make the tests pass. 仅介绍使测试通过的足够方法。
  3. It is non-peristent (request parameters is all there is to make a response, though there can be exceptions to that). 它是非持久性的(请求参数是做出响应的全部,尽管可以有例外)。 All persistance is on the client (localstorage, cookies). 所有持久性都在客户端上(本地存储,cookie)。 It is important, because simply be clearing client storage and refreshing the page - you get the fresh state of you app! 这很重要,因为只需清除客户端存储并刷新页面即可-您将获得应用程序的全新状态! Where you to test a monolith - you'd have to clear and repopulate the database, which is a hume waste of time and completely non-scalable. 在测试整体的地方-您必须清除并重新填充数据库,这浪费时间并且完全不可扩展。
  4. Embedded into the angular repository, it is a part of a testing suit and a development process. 嵌入角度存储库中,它是测试套件和开发过程的一部分。

How does that look in practice. 实际情况如何。 Let's say we are testing a Login feature of the website and it has 3 scenarios: 假设我们正在测试网站的登录功能,并且它具有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');
  });

});

To satisfy these tests all we have to do is implement an api for the POST /login request: 为了满足这些测试,我们要做的就是为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}));
  }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM