今天小编继续分享13个优秀前端测试开源框架大全,希望对从事前端的程序员以及测试岗位的测试员有所帮助。在前端测试中,框架可以把测试代码抽离出来,作为一个整体结构化地去设计测试用例,放到专门的测试文件中,也可以实现自动运行以及显示测试结果。

小编总结前端测试通常可以分为以下三种:

  1. 单元测试:将代码的各个部分分开,对软件中的最小可测试单元进行检查和验证;

  2. 集成测试:测试多个单元能否协调工作;

  3. 端到端测试(E2E):从头到尾测试整个软件产品,以确保应用程序流按预期运行。

全文大纲

  1. Jest

  2. Mocha

  3. Cypress

  4. Storybook

  5. Jasmine

  6. React Testing Library

  7. Playwright

  8. Vitest

  9. AVA

  10. Selenium

  11. Puppeteer

  12. WebdriverIO

  13. TestCafe

2022 年度 StateOfJS 官方调查结果正式公布!StateOfJS 是前端生态圈中非常有影响力的且规模较大的数据调查,前端测试框架受欢迎度排行榜,总共从四个维度(满意度、关注度、使用度、认知度)去分析,具体如下图:

StateOfJS官方满意度

StateOfJS官方关注度

StateOfJS使用度

Jest

官方网址:https://jestjs.io/

Github:https://github.com/facebook/jest

Jest 是一个令人愉快的 JavaScript 测试框架,专注于 简洁明快。

Jest 是由 Facebook 开发的 JavaScript 测试框架。它是测试 React 的首选,并且得到了 React 社区的支持和开发。

Jest 具有以下特点:

  • 兼容性:除了可以测试 React 应用,还可以轻松集成到其他应用中,与 Angular、Node、Vue 和其他基于babel的项目兼容。

  • 自动模拟:当在测试文件中导入库时,Jest 会自动模拟这些库以帮助我们轻松地使用它们。

  • 扩展 API:Jest 提供了广泛的 API,除非确实需要,否则不需要包含额外的库。

  • 计时器模拟:Jest 具有时间模拟系统,非常适合应用中的快进超时,并有助于在运行测试时节省时间。

  • 活跃社区:Jest 拥有很活跃的社区,可以帮助我们在需要时快速找到解决方案。

  • 零配置:Jest 的目标是在大部分 JavaScript 项目上实现开箱即用, 无需配置。

  • 快照:能够轻松追踪大型对象的测试。 快照可以与测试代码放在一起,也可以集成进代码 行内。

  • 隔离:测试程序拥有自己独立的进程 以最大限度地提高性能。

  • 优秀的 api:从 it 到 expect - Jest 将整个工具包放在同一个 地方。好书写、好维护、非常方便。

测试代码:

// __tests__/sum-test.jsjest.dontMock('../sum');describe('sum', function() { it('adds 1 + 2 to equal 3', function() {   var sum = require('../sum');   expect(sum(1, 2)).toBe(3);
 });
});1.2.3.4.5.6.7.8.9.

如下图:

Mocha

官方网址:https://mochajs.org/

Github:https://github.com/mochajs/mocha

Mocha 是一个功能丰富的 JavaScript 测试框架,可以运行在 Node.js 和浏览器中,使异步测试变得简单有趣。Mocha 测试连续运行,允许灵活和准确的报告,同时将未捕获的异常映射到正确的测试用例。

Mocha 不支持开箱即用的断言、模拟等,需要通过组件/插件来添加这些功能。与 Mocha 搭配的最流行的断言库包括 Chai、Assert、Should.js 和 Better-assert。

Mocha 具有以下特点:

  • 使用简单:对于不包含复杂断言或测试逻辑的较小项目,Mocha 是一个简单的解决方案。

  • ES模块支持:Mocha 支持将测试编写为 ES 模块,而不仅是使用 CommonJS。

当然,Mocha 也是有缺点的:

  • 设置难度大:必须使用额外的断言库,这确实意味着它比其他库更难设置。

  • 与插件的潜在不一致:Mocha 将测试结构包含为 globals,不必在每个文件中都使用 includeor 来节省时间。require 缺点是插件可能会要求无论如何都包含这些,从而导致不一致。

  • 不支持任意转译器:在 v6.0.0 之前,Mocha 有一个允许使用任意转译器的特性,比如 coffee-script 等,但现在已经弃用。

测试代码:

var assert = require('assert');describe('Array', function () {  describe('#indexOf()', function () {    it('should return -1 when the value is not present', function () {      assert.equal([1, 2, 3].indexOf(4), -1);
    });
  });
});1.2.3.4.5.6.7.8.

如下图:

Cypress

官方网址:https://cypress.io/

Github:https://github.com/cypress-io/cypress

Cypress 是为现代 Web 构建的下一代前端测试工具。借助 Cypress,开发人员可以编写端到端测试、集成测试和单元测试。Cypress 完全可以在真正的浏览器(Chrome、Firefox 和 Edge)中运行,不需要驱动程序二进制文件。自动化代码和应用代码共享同一个平台,让开发人员可以完全控制被测应用。Cypress 以其端到端测试功能而闻名,这意味着可以遵循预定义的用户行为,并让该工具在每次部署新代码时报告潜在差异。

Cypress 具有以下特点:

  • 端到端测试:由于 Cypress 在真实浏览器中运行,因此可以依赖它进行端到端用户测试。

  • 时间轴快照测试:在执行时,Cypress 会拍下那一刻的快照,并允许开发人员或 QA 测试人员查看特定步骤发生的情况。

  • 稳定可靠:与其他测试框架相比,Cypress 提供了稳定可靠的测试执行结果。

  • 文档和社区:从零到运行,Cypress 包含所有必要的信息以帮助开发人员加快速度,并且它还有一个活跃的社区。

  • 速度快:Cypress 的测试执行速度很快,响应时间不短 20 毫秒。

不过,需要注意的是,Cypress 只能在单个浏览器中运行测试。

测试代码:

describe('My First Test', () => {  it('Gets, types and asserts', () => {    cy.visit('https://example.cypress.io')    cy.contains('type').click()    // Should be on a new URL which
    // includes '/commands/actions'
    cy.url().should('include', '/commands/actions')    // Get an input, type into it
    cy.get('.action-email').type('fake@email.com')    //  Verify that the value has been updated
    cy.get('.action-email').should('have.value', 'fake@email.com')
  })
})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.

如下图:

Storybook

官方网址:https://storybook.js.org/

Github:https://github.com/storybookjs/storybook

Storybook与其他 JavaScript 测试框架不同,Storybook 是一个 UI 测试工具,它为测试组件提供了一个隔离的环境。Storybook 还附带工具、Test runner 以及与更大的 JavaScript 生态系统的方便集成,以扩展 UI 测试覆盖范围。

可以通过多种方式使用 Storybook 进行 UI 测试:

  • 视觉测试:捕获每个故事的屏幕截图,然后将其与基线进行比较以检测外观和集成问题。

  • 辅助功能测试:发现与视觉、听觉、移动、认知、语言或神经障碍相关的可用性问题。

  • 交互测试:通过模拟用户行为、触发事件并确保状态按预期更新来验证组件功能。

  • 快照测试:检测渲染标记中的更改以显示表面渲染错误或警告。

  • 将其他测试中的故事导入 QA 甚至更多 UI 特性。

测试代码:

// stories/userStory.jsimport { storiesOf } from "@storybook/react";import { userHooks } from '../src/index'import React from 'react';const Demo = () {    const result = userHooks();    return (        <div>
            <p>{result}</p>
        </div>
    )
}storiesOf("user", module).add('Demo', Demo);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

如下图:

Jasmine

官方网址:http://jasmine.github.io/

Github:https://github.com/jasmine/jasmine

Jasmine 是一个简易的 JavaScript 单元测试框架,其不依赖于任何浏览器、DOM、或者是任何 JavaScript 而存在。它适用于所有网站、Node.js 项目,或者是任何能够在 JavaScript 上面运行的程序。Jasmine 以行为驱动开发 (BDD) 工具而闻名。BDD 涉及在编写实际代码之前编写测试(与测试驱动开发 (TDD)相反)。

Jasmine 具有以下特点:

  • API 简单:它提供了简洁且易于理解的语法,以及用于编写单元测试的丰富而直接的 API 。

  • 开箱即用:不需要额外的断言或模拟库,开箱即用。

  • 速度快:由于不依赖任何外部库,因此速度相对较快。

  • 多语言:不仅用于编写 JS 测试,也可以用于 Ruby(通过Jasmine-gem)或 Python(通过Jsmin-py)

当然,Jasmine 也是有有缺点的:

  • 污染全局环境:默认情况下,它会创建测试全局变量(关键字如“describe”或“test”),因此不必在测试中导入它们。在特定情况下,这可能会成为不利因素。

  • 编写异步测试具有挑战性:使用 Jasmine 测试异步函数比较困难。

测试代码:

describe("A suite is just a function", function() {  let a;  it("and so is a spec", function() {    a = true;    expect(a).toBe(true);
  });
});1.2.3.4.5.6.7.8.9.

如下图:

React Testing Library

官方网址:https://testing-library.com/react

Github:https://github.com/testing-library/react-testing-library

React Testing Library 基于 DOM Testing Library 的基础上添加一些 API,主要用于测试 React 组件。该库在使用过程并不关注组件的内部实现,而是更关注测试。该库基于 react-dom 和 react-dom/test-utils,是以上两者的轻量实现。

React Testing Library 不像 Jest 那样是一个 Test runner。事实上,它们可以协同工作。React Testing Library 是一组工具和功能,可帮助访问 DOM 并对其执行操作,即将组件渲染到虚拟 DOM 中,搜索并与之交互。

React Testing Library 具有以下特点:

  • React 官方推荐:可以在 React 的官方文档中找到使用此库的参考和建议。

  • 尺寸小:它是专门为测试 React 应用/组件而编写的。

测试代码:

__tests__/fetch.test.jsximport React from 'react'import {rest} from 'msw'import {setupServer} from 'msw/node'import {render, fireEvent, waitFor, screen} from '@testing-library/react'import '@testing-library/jest-dom'import Fetch from '../fetch'const server = setupServer(  rest.get('/greeting', (req, res, ctx) {    return res(ctx.json({greeting: 'hello there'}))
  }),
)beforeAll(() server.listen())afterEach(() server.resetHandlers())afterAll(() server.close())test('loads and displays greeting', async () => {  render(<Fetch url="/greeting" />)  fireEvent.click(screen.getByText('Load Greeting'))  await waitFor(() screen.getByRole('heading'))  expect(screen.getByRole('heading')).toHaveTextContent('hello there')  expect(screen.getByRole('button')).toBeDisabled()
})test('handles server error', async () => {  server.use(    rest.get('/greeting', (req, res, ctx) {      return res(ctx.status(500))
    }),
  )  render(<Fetch url="/greeting" />)  fireEvent.click(screen.getByText('Load Greeting'))  await waitFor(() screen.getByRole('alert'))  expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')  expect(screen.getByRole('button')).not.toBeDisabled()
})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.

如下图:

Playwright

官方网址:https://playwright.dev/

Github: https://github.com/microsoft/playwright

Playwright 是一个用于端到端测试的自动化框架。该框架由 Microsoft 构建和维护,旨在跨主要浏览器引擎(Chromium、Webkit 和 Firefox)运行。它实际上是早期 Puppeteer 项目的一个分支。主要区别在于,Playwright 是专门为开发人员和测试人员进行 E2E 测试而编写的。Playwright 还可以与主要的 CI/CD 服务器一起使用,如 TravisCI、CircleCI、Jenkins、Appveyor、GitHub Actions 等。

Playwright 具有以下特点:

  • 多语言:Playwright 支持 JavaScript、Java、Python 和 .NET C# 等多种语言;

  • 多个 Test Runner 支持:可以被 Mocha、Jest 和 Jasmine 使用;

  • 跨浏览器:该框架的主要目标是支持所有主流浏览器。

  • 模拟和原生事件支持:可以模拟移动设备、地理位置和权限,还支持利用鼠标和键盘的原生输入事件。

当然,Playwright 也有一些缺点:

  • 仍处于早期阶段:相当较新,社区支持有限;

  • 不支持真实设备:不支持用于移动浏览器测试的真实设备,但支持模拟器。

测试代码:

import { test, expect } from '@playwright/test';test('my test', async ({ page }) => {  await page.goto('https://playwright.dev/');  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);  // Expect an attribute "to be strictly equal" to the value.
  await expect(page.locator('text=Get Started').first()).toHaveAttribute('href', '/docs/intro');  await page.click('text=Get Started');  // Expect some text to be visible on the page.
  await expect(page.locator('text=Introduction').first()).toBeVisible();
});1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.

如下图:

Vitest

官方网址:https://vitest.dev/

Github: https://github.com/avajs/ava

Vitest 是一个由 Vite 提供支持的极速单元测试框架。其和 Vite 的配置、转换器、解析器和插件保持一致、开箱即用的 TypeScript / JSX 支持、支持 Smart 和 instant watch 模式,如同用于测试的 HMR、内置 Tinyspy 用于模拟、打标和监察等。Vitest 非常关心性能,使用 Worker 线程尽可能并行运行,带来更好的开发者体验。

Vitest 具有以下特点:

  • Vite 支持:重复使用 Vite 的配置、转换器、解析器和插件,在应用和测试中保持一致。

  • 兼容 Jest:拥有预期、快照、覆盖等 - 从 Jest 迁移很简单。

  • 智能即时浏览模式:智能文件监听模式,就像是测试的 HMR。

  • ESM, TypeScript, JSX:由 esbuild 提供的开箱即用 ESM、TypeScript 和 JSX 支持。

  • 源内测试:提供了一种在源代码中运行测试以及实现的方法,类似于 Rust 的模块测试。

过,Vitest 仍处于早期阶段(最新版本为 0.28.1)。尽管 Vitest 背后的团队在创建此工具方面做了大量工作,但它还很年轻,社区支持可能还不是很完善。

测试代码:

import { assert, describe, it } from 'vitest'// Only this suite (and others marked with only) are rundescribe.only('suite', () {  it('test', () {    assert.equal(Math.sqrt(4), 3)
  })
})describe('another suite', () {  it('skipped test', () {    // Test skipped, as tests are running in Only mode
    assert.equal(Math.sqrt(4), 3)
  })  it.only('test', () {    // Only this test (and others marked with only) are run
    assert.equal(Math.sqrt(4), 2)
  })
})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

如下图:

AVA

Github: https://github.com/avajs/ava

AVA 是一个极简的 Test Runner,它利用 JavaScript 的异步特性并同时运行测试,从而提高性能。AVA 不会为创建任何 Globals,因此可以更轻松地控制使用的内容。这可以使测试更加清晰,确保确切知道发生了什么。

AVA 具有以下特点:

  • 同时运行测试:利用 JavaScript 的异步特性使得测试变得简单,最小化部署之间的等待时间;

  • 简单的 API:通过了一个简单的 API,仅提供需要的内容;

  • 快照测试:通过 jest-snapshot 提供,当想知道应用的 UI 何时意外更改时,这非常有用;

  • Tap 格式报告:Ava 默认显示可读的报告,也可以获得 TAP 格式的报告。

当然,AVA 也有一些缺点:

  • 没有测试分组:Ava 无法将相似的测试组合在一起。

  • 没有内置的模拟:Ava 未附带模拟,不过可以使用第三方库(如Sinon.js)。

测试代码:

import test from 'ava';test('foo', t {    t.pass();
});test('bar', async t => {    const bar = Promise.resolve('bar');    t.is(await bar, 'bar');
});1.2.3.4.5.6.7.8.9.10.11.

如下图:

Selenium

官方网址:https://www.selenium.dev/

Github: https://github.com/seleniumhq/selenium

Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla FirefoxSafariGoogle ChromeOpera,Edge等。这个工具的主要功能包括:测试与浏览器的兼容性——测试应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成.Net、JavaPerl等不同语言的测试脚本。

功能

  • 框架底层使用JavaScript模拟真实用户对浏览器进行操作。测试脚本执行时,浏览器自动按照脚本代码做出点击,输入,打开,验证等操作,就像真实用户所做的一样,从终端用户的角度测试应用程序。

  • 使浏览器兼容性测试自动化成为可能,尽管在不同的浏览器上依然有细微的差别。

  • 使用简单,可使用Java,Python等多种语言编写用例脚本。

优势

据Selenium主页所说,与其他测试工具相比,使用Selenium的最大好处是:

Selenium测试直接在浏览器中运行,就像真实用户所做的一样。Selenium测试可以在Windows、Linux和Macintosh上的Internet Explorer、Chrome和Firefox中运行。其他测试工具都不能覆盖如此多的平台。使用Selenium和在浏览器中运行测试还有很多其他好处。

Selenium完全开源,对商业用户也没有任何限制,支持分布式,拥有成熟的社区与学习文档

下面是主要的几大好处:

通过编写模仿用户操作的Selenium测试脚本,可以从终端用户的角度来测试应用程序。通过在不同浏览器中运行测试,更容易发现浏览器的不兼容性。Selenium的核心,也称browser bot,是用JavaScript编写的。这使得测试脚本可以在受支持的浏览器中运行。browser bot负责执行从测试脚本接收到的命令,测试脚本要么是用HTML的表布局编写的,要么是使用一种受支持的编程语言编写的。

测试代码:

如下图:

Puppeteer

官方网址:https://pptr.dev/

Github: https://github.com/puppeteer/puppeteer

Puppeteer 是一个控制 headless Chrome 的 Node.js API 。它是一个 Node.js 库,通过 DevTools 协议提供了一个高级的 API 来控制 headless Chrome。它还可以配置为使用完整的(非 headless)Chrome。

在浏览器中手动完成的大多数事情都可以通过使用 Puppeteer 完成,下面是一些入门的例子:

  • 生成屏幕截图和 PDF 页面

  • 检索 SPA 并生成预渲染内容(即 “SSR”)

  • 从网站上爬取内容

  • 自动提交表单,UI 测试,键盘输入等

  • 创建一个最新的自动测试环境。使用最新的 JavaScript 和浏览器功能,在最新版本的 Chrome 中直接运行测试

  • 捕获网站的时间线跟踪,以帮助诊断性能问题

测试代码:

import puppeteer from 'puppeteer';

(async () => {  const browser = await puppeteer.launch();  const page = await browser.newPage();  await page.goto('https://developer.chrome.com/');  // Set screen size
  await page.setViewport({width: 1080, height: 1024});  // Type into search box
  await page.type('.search-box__input', 'automate beyond recorder');  // Wait and click on first result
  const searchResultSelector = '.search-box__link';  await page.waitForSelector(searchResultSelector);  await page.click(searchResultSelector);  // Localte the full title with a unique string
  const textSelector = await page.waitForSelector(    'text/Customize and automate'
  );  const fullTitle = await textSelector.evaluate(el => el.textContent);  // Print the full title
  console.log('The title of this blog post is "%s".', fullTitle);  await browser.close();
})();1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.

如下图:

WebdriverIO

官方网址:https://webdriver.io/

Github: https://github.com/webdriverio/webdriverio

WebdriverIO是一个基于节点的浏览器和自动化测试框架。js。在WebDrivero中添加助手函数在WebDrivero中很简单。此外,它还可以在WebDriver协议和Chrome Devtools协议上运行,这使得它对于基于Selenium WebDriver的跨浏览器都非常有效 testing或者基于铬的自动化。最重要的是,由于WebDriverIO是开源的,您可以获得一系列插件来满足您的自动化需求。
专门为前端开发者们:可扩展-添加助手函数或更复杂的集合以及现有命令的组合非常简单,非常有用。

兼容-WebdriverIO可以在WebDriver协议上运行,以进行真正的跨浏览器测试,也可以在Chrome DevTools协议上使用Puppeter进行基于Chromium的自动化。

功能丰富-各种内置和社区插件允许您轻松集成和扩展设置以满足您的需求。

您可以使用WebdriverIO自动化:

  • 用React、Vue、Angular、Svelte或其他前端框架编写的现代web应用程序

  • 在模拟器/模拟器或真实设备上运行的混合或本地移动应用程序

  • 本机桌面应用程序(例如用Electron.js编写)

  • 浏览器中web组件的单元或组件测试

测试代码:

import { $, expect } from '@wdio/globals'import { render } from '@testing-library/vue'import HelloWorld from '../../src/components/HelloWorld.vue'describe('Vue Component Testing', () {    it('increments value on click', async () => {        const { getByText } = render(HelloWorld)        const btn = getByText('count is 0')        // transform into WebdriverIO element
        const button = await $(btn)        await button.click()        await button.click()        getByText('count is 2')        await expect($('button=count is 2')).toExist()
    })
})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.

如下图:

TestCafe

官方网址:https://testcafe.io/

Github:https://github.com/DevExpress/testcafe

TestCafe 是一个用于测试 Web 应用程序的纯 Node.js 端到端解决方案。 它负责所有阶段:启动浏览器,运行测试,收集测试结果和生成报告。 TestCafe 不需要浏览器插件,它在所有流行的现代浏览器开箱即用。

前面小编介绍了很多测试框架工具,为什么还要介绍TestCafe呢?因为TestCafe支持的浏览器更多,且也内置自动等待机制,稳定性上面相较与webdriver有很大提高。下面列举了TestCafe在稳定性、测试数据管理、配置信息管理以及支持的web应用操作场景等方面的支持程度。另外,TestCafe还支持并发执行,即可以同时开启多个浏览器运行多个测试案例,缩短测试反馈时间,这是前面两个框架都不具备的。故如果被测应用需要支持IE、safari这些浏览器,testcafe是个不错的选择。

除上面已支持的功能外,testcafe还计划支持。

Testing in Multiple Browser Windows,即开启多个tab页,在多个tab页上执行一个完整的自动化测试。 --计划支持,并已进入开发阶段。

框架自身支持接口调用。 --计划支持,暂未进入开发阶段。

Selector Debug Panel,UI测试大部分调试场景都是定位和操作页面元素,Selector Debug Panel可极大的提升调试效率。 --计划支持,并已进入开发阶段。

测试代码:

const createTestCase = require('testcafe');const fs = require('fs');let testcafe = null;createTestCase('localhost', 1337, 1338)
    .then(tc {        testcafe = tc;        const runner = testcafe.createRunner();        const stream = fs.createWriteStream('report.json');        return runner
            // 需要运行的cases
            .src(
                [                    '../demo/podemo/*.js',                    '../demo/setWindowsSize.js'
                ]
            )            // 设置需要执行的浏览器
            .browsers([                'chrome',                'firefox'
            ])            // 错误自动截图
            .screenshots(                // 保存路径
                '../error/',                true,                // 保存路劲格式
                '${DATE}_${TIME}/test-${TEST_INDEX}/${USERAGENT}/${FILE_INDEX}.png'
            )            // 生成report格式,根据需要安装对应report模块,
            // 详细看:http://devexpress.github.io/testcafe/documentation/using-testcafe/common-concepts/reporters.html
            .reporter('json', stream)            // 并发
            .concurrency(3)
            .run({                skipJsErrors: true, // 页面js错误是否忽略,建议为true
                quarantineMode: true, // 隔离模式,可以理解为失败重跑
                selectorTimeout: 15000, // 设置页面元素查找超时时间,智能等待
                assertionTimeout: 7000, // 设置断言超时时间
                pageLoadTimeout: 30000, // 设置页面加载超时时间
                debugOnFail: true, // 失败开启调试模式 脚本编写建议开启
                speed: 1 // 执行速度0.01 - 1
            });
    }).then(failedCount {    console.error('Failed Count:' + failedCount);    testcafe.close();
})
    .catch(err {        console.error(err);
    });1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.

如下图: