标签 测试 下的文章

使用 Python behave 框架的行为驱动开发模式可以帮助你的团队更好的协作和测试自动化。

您是否听说过 行为驱动开发 behavior-driven development (BDD),并好奇这是个什么东西?也许你发现了团队成员在谈论“嫩瓜”(LCTT 译注:“ 嫩瓜 gherkin ” 是一种简单的英语文本语言,工具 cucumber 通过解释它来执行测试脚本,见下文),而你却不知所云。或许你是一个 Python 人 Pythonista ,正在寻找更好的方法来测试你的代码。 无论在什么情况下,了解 BDD 都可以帮助您和您的团队实现更好的协作和测试自动化,而 Python 的 behave 框架是一个很好的起点。

什么是 BDD?

在软件中,行为是指在明确定义的输入、动作和结果场景中功能是如何运转的。 产品可以表现出无数的行为,例如:

  • 在网站上提交表单
  • 搜索想要的结果
  • 保存文档
  • 进行 REST API 调用
  • 运行命令行界面命令

根据产品的行为定义产品的功能可以更容易地描述产品,并对其进行开发和测试。 BDD 的核心是:使行为成为软件开发的焦点。在开发早期使用示例语言的规范来定义行为。最常见的行为规范语言之一是 Gherkin,Cucumber项目中的Given-When-Then场景格式。 行为规范基本上是对行为如何工作的简单语言描述,具有一致性和焦点的一些正式结构。 通过将步骤文本“粘合”到代码实现,测试框架可以轻松地自动化这些行为规范。

下面是用Gherkin编写的行为规范的示例:

根据产品的行为定义产品的功能可以更容易地描述产品,开发产品并对其进行测试。 这是BDD的核心:使行为成为软件开发的焦点。 在开发早期使用示例规范的语言来定义行为。 最常见的行为规范语言之一是Gherkin,来自 Cucumber 项目中的 Given-When-Then 场景格式。 行为规范基本上是对行为如何工作的简单语言描述,具有一致性和聚焦点的一些正式结构。 通过将步骤文本“粘合”到代码实现,测试框架可以轻松地自动化这些行为规范。

下面是用 Gherkin 编写的行为规范的示例:

Scenario: Basic DuckDuckGo Search
  Given the DuckDuckGo home page is displayed
  When the user searches for "panda"
  Then results are shown for "panda"

快速浏览一下,行为是直观易懂的。 除少数关键字外,该语言为自由格式。 场景简洁而有意义。 一个真实的例子说明了这种行为。 步骤以声明的方式表明应该发生什么——而不会陷入如何如何的细节中。

BDD 的主要优点是良好的协作和自动化。 每个人都可以为行为开发做出贡献,而不仅仅是程序员。从流程开始就定义并理解预期的行为。测试可以与它们涵盖的功能一起自动化。每个测试都包含一个单一的、独特的行为,以避免重复。最后,现有的步骤可以通过新的行为规范重用,从而产生雪球效果。

Python 的 behave 框架

behave 是 Python 中最流行的 BDD 框架之一。 它与其他基于 Gherkin 的 Cucumber 框架非常相似,尽管没有得到官方的 Cucumber 定名。 behave 有两个主要层:

  1. 用 Gherkin 的 .feature 文件编写的行为规范
  2. 用 Python 模块编写的步骤定义和钩子,用于实现 Gherkin 步骤

如上例所示,Gherkin 场景有三部分格式:

  1. 鉴于(Given)一些初始状态
  2. 每当(When)行为发生时
  3. 然后(Then)验证结果

当 behave 运行测试时,每个步骤由装饰器“粘合”到 Python 函数。

安装

作为先决条件,请确保在你的计算机上安装了 Python 和 pip。 我强烈建议使用 Python 3.(我还建议使用 pipenv,但以下示例命令使用更基本的 pip。)

behave 框架只需要一个包:

pip install behave

其他包也可能有用,例如:

pip install requests    # 用于调用 REST API
pip install selenium    # 用于 web 浏览器交互

GitHub 上的 behavior-driven-Python 项目包含本文中使用的示例。

Gherkin 特点

behave 框架使用的 Gherkin 语法实际上是符合官方的 Cucumber Gherkin 标准的。.feature 文件包含了功能(Feature)部分,而场景部分又包含具有 Given-When-Then 步骤的场景(Scenario) 部分。 以下是一个例子:

Feature: Cucumber Basket
  As a gardener,
  I want to carry many cucumbers in a basket,
  So that I don’t drop them all.

  @cucumber-basket
  Scenario: Add and remove cucumbers
    Given the basket is empty
    When "4" cucumbers are added to the basket
    And "6" more cucumbers are added to the basket
    But "3" cucumbers are removed from the basket
    Then the basket contains "7" cucumbers

这里有一些重要的事情需要注意:

  • FeatureScenario 部分都有简短的描述性标题
  • 紧跟在 Feature 标题后面的行是会被 behave 框架忽略掉的注释。将功能描述放在那里是一种很好的做法。
  • ScenarioFeature 可以有标签(注意 @cucumber-basket 标记)用于钩子和过滤(如下所述)。
  • 步骤都遵循严格的 Given-When-Then 顺序
  • 使用 AndBut 可以为任何类型添加附加步骤。
  • 可以使用输入对步骤进行参数化——注意双引号里的值。

通过使用场景大纲(Scenario Outline),场景也可以写为具有多个输入组合的模板:

Feature: Cucumber Basket

  @cucumber-basket
  Scenario Outline: Add cucumbers
    Given the basket has “<initial>” cucumbers
    When "<more>" cucumbers are added to the basket
    Then the basket contains "<total>" cucumbers

    Examples: Cucumber Counts
      | initial | more | total |
      |    0    |   1  |   1   |
      |    1    |   2  |   3   |
      |    5    |   4  |   9   |

场景大纲总是有一个示例(Examples)表,其中第一行给出列标题,后续每一行给出一个输入组合。 只要列标题出现在由尖括号括起的步骤中,行值就会被替换。 在上面的示例中,场景将运行三次,因为有三行输入组合。 场景大纲是避免重复场景的好方法。

Gherkin 语言还有其他元素,但这些是主要的机制。 想了解更多信息,请阅读 Automation Panda 这个网站的文章 Gherkin by ExampleWriting Good Gherkin

Python 机制

每个 Gherkin 步骤必须“粘合”到步骤定义——即提供了实现的 Python 函数。 每个函数都有一个带有匹配字符串的步骤类型装饰器。它还接收共享的上下文和任何步骤参数。功能文件必须放在名为 features/ 的目录中,而步骤定义模块必须放在名为 features/steps/ 的目录中。 任何功能文件都可以使用任何模块中的步骤定义——它们不需要具有相同的名称。 下面是一个示例 Python 模块,其中包含 cucumber basket 功能的步骤定义。

from behave import *
from cucumbers.basket import CucumberBasket

@given('the basket has "{initial:d}" cucumbers')
def step_impl(context, initial):
    context.basket = CucumberBasket(initial_count=initial)

@when('"{some:d}" cucumbers are added to the basket')
def step_impl(context, some):
    context.basket.add(some)

@then('the basket contains "{total:d}" cucumbers')
def step_impl(context, total):
    assert context.basket.count == total

可以使用三个步骤匹配器parsecfparsere。默认的,也是最简单的匹配器是 parse,如上例所示。注意如何解析参数化值并将其作为输入参数传递给函数。一个常见的最佳实践是在步骤中给参数加双引号。

每个步骤定义函数还接收一个上下文变量,该变量保存当前正在运行的场景的数据,例如 featurescenariotags 字段。也可以添加自定义字段,用于在步骤之间共享数据。始终使用上下文来共享数据——永远不要使用全局变量!

behave 框架还支持钩子来处理 Gherkin 步骤之外的自动化问题。钩子是一个将在步骤、场景、功能或整个测试套件之前或之后运行的功能。钩子让人联想到面向方面的编程。它们应放在 features/ 目录下的特殊 environment.py 文件中。钩子函数也可以检查当前场景的标签,因此可以有选择地应用逻辑。下面的示例显示了如何使用钩子为标记为 @web 的任何场景生成和销毁一个 Selenium WebDriver 实例。

from selenium import webdriver

def before_scenario(context, scenario):
    if 'web' in context.tags:
        context.browser = webdriver.Firefox()
        context.browser.implicitly_wait(10)

def after_scenario(context, scenario):
    if 'web' in context.tags:
        context.browser.quit()

注意:也可以使用 fixtures 进行构建和清理。

要了解一个 behave 项目应该是什么样子,这里是示例项目的目录结构:

任何 Python 包和自定义模块都可以与 behave 框架一起使用。 使用良好的设计模式构建可扩展的测试自动化解决方案。步骤定义代码应简明扼要。

运行测试

要从命令行运行测试,请切换到项目的根目录并运行 behave 命令。 使用 -help 选项查看所有可用选项。

以下是一些常见用例:

# run all tests
behave

# run the scenarios in a feature file
behave features/web.feature

# run all tests that have the @duckduckgo tag
behave --tags @duckduckgo

# run all tests that do not have the @unit tag
behave --tags ~@unit

# run all tests that have @basket and either @add or @remove
behave --tags @basket --tags @add,@remove

为方便起见,选项可以保存在 config 文件中。

其他选择

behave 不是 Python 中唯一的 BDD 测试框架。其他好的框架包括:

  • pytest-bdd,是 pytest 的插件,和 behave 一样,它使用 Gherkin 功能文件和步骤定义模块,但它也利用了 pytest 的所有功能和插件。例如,它可以使用 pytest-xdist 并行运行 Gherkin 场景。 BDD 和非 BDD 测试也可以与相同的过滤器一起执行。pytest-bdd 还提供更灵活的目录布局。
  • radish 是一个 “Gherkin 增强版”框架——它将场景循环和前提条件添加到标准的 Gherkin 语言中,这使得它对程序员更友好。它还像 behave 一样提供了丰富的命令行选项。
  • lettuce 是一种较旧的 BDD 框架,与 behave 非常相似,在框架机制方面存在细微差别。然而,GitHub 最近显示该项目的活动很少(截至2018 年 5 月)。

任何这些框架都是不错的选择。

另外,请记住,Python 测试框架可用于任何黑盒测试,即使对于非 Python 产品也是如此! BDD 框架非常适合 Web 和服务测试,因为它们的测试是声明性的,而 Python 是一种很好的测试自动化语言

本文基于作者的 PyCon Cleveland 2018 演讲“行为驱动的Python”。


via: https://opensource.com/article/18/5/behavior-driven-python

作者:Andrew Knight 选题:lujun9972 译者:Flowsnow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

超过 3 亿用户正在使用 Stream。这些用户全都依赖我们的框架,而我们十分擅长测试要放到生产环境中的任何东西。我们大部分的代码库是用 Go 语言编写的,剩下的部分则是用 Python 编写。

我们最新的展示应用,Winds 2.0,是用 Node.js 构建的,很快我们就了解到测试 Go 和 Python 的常规方法并不适合它。而且,创造一个好的测试套件需要用 Node.js 做很多额外的工作,因为我们正在使用的框架没有提供任何内建的测试功能。

不论你用什么语言,要构建完好的测试框架可能都非常复杂。本文我们会展示 Node.js 测试过程中的困难部分,以及我们在 Winds 2.0 中用到的各种工具,并且在你要编写下一个测试集合时为你指明正确的方向。

为什么测试如此重要

我们都向生产环境中推送过糟糕的提交,并且遭受了其后果。碰到这样的情况不是好事。编写一个稳固的测试套件不仅仅是一个明智的检测,而且它还让你能够完全地重构代码,并自信重构之后的代码仍然可以正常运行。这在你刚刚开始编写代码的时候尤为重要。

如果你是与团队共事,达到测试覆盖率极其重要。没有它,团队中的其他开发者几乎不可能知道他们所做的工作是否导致重大变动(或破坏)。

编写测试同时会促进你和你的队友把代码分割成更小的片段。这让别人去理解你的代码和修改 bug 变得容易多了。产品收益变得更大,因为你能更早的发现 bug。

最后,没有测试,你的基本代码还不如一堆纸片。基本不能保证你的代码是稳定的。

困难的部分

在我看来,我们在 Winds 中遇到的大多数测试问题是 Node.js 中特有的。它的生态系统一直在变大。例如,如果你用的是 macOS,运行 brew upgrade(安装了 homebrew),你看到你一个新版本的 Node.js 的概率非常高。由于 Node.js 迭代频繁,相应的库也紧随其后,想要与最新的库保持同步非常困难。

以下是一些马上映入脑海的痛点:

  1. 在 Node.js 中进行测试是非常主观而又不主观的。人们对于如何构建一个测试架构以及如何检验成功有不同的看法。沮丧的是还没有一个黄金准则规定你应该如何进行测试。
  2. 有一堆框架能够用在你的应用里。但是它们一般都很精简,没有完好的配置或者启动过程。这会导致非常常见的副作用,而且还很难检测到;所以你最终会想要从零开始编写自己的 测试执行平台 test runner 测试执行平台。
  3. 几乎可以保证你 需要 编写自己的测试执行平台(马上就会讲到这一节)。

以上列出的情况不是理想的,而且这是 Node.js 社区应该尽管处理的事情。如果其他语言解决了这些问题,我认为也是作为广泛使用的语言, Node.js 解决这些问题的时候。

编写你自己的测试执行平台

所以……你可能会好奇test runner测试执行平台 什么,说实话,它并不复杂。测试执行平台是测试套件中最高层的容器。它允许你指定全局配置和环境,还可以导入配置。可能有人觉得做这个很简单,对吧?别那么快下结论。

我们所了解到的是,尽管现在就有足够多的测试框架了,但没有一个测试框架为 Node.js 提供了构建你的测试执行平台的标准方式。不幸的是,这需要开发者来完成。这里有个关于测试执行平台的需求的简单总结:

  • 能够加载不同的配置(比如,本地的、测试的、开发的),并确保你 永远不会 加载一个生产环境的配置 —— 你能想象出那样会出什么问题。
  • 播种数据库——产生用于测试的数据。必须要支持多种数据库,不论是 MySQL、PostgreSQL、MongoDB 或者其它任何一个数据库。
  • 能够加载配置(带有用于开发环境测试的播种数据的文件)。

开发 Winds 的时候,我们选择 Mocha 作为测试执行平台。Mocha 提供了简单并且可编程的方式,通过命令行工具(整合了 Babel)来运行 ES6 代码的测试。

为了进行测试,我们注册了自己的 Babel 模块引导器。这为我们提供了更细的粒度,更强大的控制,在 Babel 覆盖掉 Node.js 模块加载过程前,对导入的模块进行控制,让我们有机会在所有测试运行前对模块进行模拟。

此外,我们还使用了 Mocha 的测试执行平台特性,预先把特定的请求赋给 HTTP 管理器。我们这么做是因为常规的初始化代码在测试中不会运行(服务器交互是用 Chai HTTP 插件模拟的),还要做一些安全性检查来确保我们不会连接到生产环境数据库。

尽管这不是测试执行平台的一部分,有一个 配置 fixture 加载器也是我们测试套件中的重要的一部分。我们试验过已有的解决方案;然而,我们最终决定编写自己的助手程序,这样它就能贴合我们的需求。根据我们的解决方案,在生成或手动编写配置时,通过遵循简单专有的协议,我们就能加载数据依赖很复杂的配置。

Winds 中用到的工具

尽管过程很冗长,我们还是能够合理使用框架和工具,使得针对后台 API 进行的适当测试变成现实。这里是我们选择使用的工具:

Mocha

Mocha,被称为 “运行在 Node.js 上的特性丰富的测试框架”,是我们用于该任务的首选工具。拥有超过 15K 的星标,很多支持者和贡献者,我们知道对于这种任务,这是正确的框架。

Chai

然后是我们的断言库。我们选择使用传统方法,也就是最适合配合 Mocha 使用的 —— Chai。Chai 是一个用于 Node.js,适合 BDD 和 TDD 模式的断言库。拥有简单的 API,Chai 很容易整合进我们的应用,让我们能够轻松地断言出我们 期望 从 Winds API 中返回的应该是什么。最棒的地方在于,用 Chai 编写测试让人觉得很自然。这是一个简短的例子:

describe('retrieve user', () => {
    let user;

    before(async () => {
        await loadFixture('user');
        user = await User.findOne({email: authUser.email});
        expect(user).to.not.be.null;
    });

    after(async () => {
        await User.remove().exec();
    });

    describe('valid request', () => {
        it('should return 200 and the user resource, including the email field, when retrieving the authenticated user', async () => {
            const response = await withLogin(request(api).get(`/users/${user._id}`), authUser);

            expect(response).to.have.status(200);
            expect(response.body._id).to.equal(user._id.toString());
        });

        it('should return 200 and the user resource, excluding the email field, when retrieving another user', async () => {
            const anotherUser = await User.findOne({email: '[email protected]'});

            const response = await withLogin(request(api).get(`/users/${anotherUser.id}`), authUser);

            expect(response).to.have.status(200);
            expect(response.body._id).to.equal(anotherUser._id.toString());
            expect(response.body).to.not.have.an('email');
        });

    });

    describe('invalid requests', () => {

        it('should return 404 if requested user does not exist', async () => {
            const nonExistingId = '5b10e1c601e9b8702ccfb974';
            expect(await User.findOne({_id: nonExistingId})).to.be.null;

            const response = await withLogin(request(api).get(`/users/${nonExistingId}`), authUser);
            expect(response).to.have.status(404);
        });
    });

});

Sinon

拥有与任何单元测试框架相适应的能力,Sinon 是模拟库的首选。而且,精简安装带来的超级整洁的整合,让 Sinon 把模拟请求变成了简单而轻松的过程。它的网站有极其良好的用户体验,并且提供简单的步骤,供你将 Sinon 整合进自己的测试框架中。

Nock

对于所有外部的 HTTP 请求,我们使用健壮的 HTTP 模拟库 nock,在你要和第三方 API 交互时非常易用(比如说 Stream 的 REST API)。它做的事情非常酷炫,这就是我们喜欢它的原因,除此之外关于这个精妙的库没有什么要多说的了。这是我们的速成示例,调用我们在 Stream 引擎中提供的 personalization

nock(config.stream.baseUrl)
    .get(/winds_article_recommendations/)
    .reply(200, { results: [{foreign_id:`article:${article.id}`}] });

Mock-require

mock-require 库允许依赖外部代码。用一行代码,你就可以替换一个模块,并且当代码尝试导入这个库时,将会产生模拟请求。这是一个小巧但稳定的库,我们是它的超级粉丝。

Istanbul

Istanbul 是 JavaScript 代码覆盖工具,在运行测试的时候,通过模块钩子自动添加覆盖率,可以计算语句,行数,函数和分支覆盖率。尽管我们有相似功能的 CodeCov(见下一节),进行本地测试时,这仍然是一个很棒的工具。

最终结果 — 运行测试

有了这些库,还有之前提过的测试执行平台,现在让我们看看什么是完整的测试(你可以在 这里 看看我们完整的测试套件):

import nock from 'nock';
import { expect, request } from 'chai';

import api from '../../src/server';
import Article from '../../src/models/article';
import config from '../../src/config';
import { dropDBs, loadFixture, withLogin } from '../utils.js';

describe('Article controller', () => {
    let article;

    before(async () => {
        await dropDBs();
        await loadFixture('initial-data', 'articles');
        article = await Article.findOne({});
        expect(article).to.not.be.null;
        expect(article.rss).to.not.be.null;
    });

    describe('get', () => {
        it('should return the right article via /articles/:articleId', async () => {
            let response = await withLogin(request(api).get(`/articles/${article.id}`));
            expect(response).to.have.status(200);
        });
    });

    describe('get parsed article', () => {
        it('should return the parsed version of the article', async () => {
            const response = await withLogin(
                request(api).get(`/articles/${article.id}`).query({ type: 'parsed' })
            );
            expect(response).to.have.status(200);
        });
    });

    describe('list', () => {
        it('should return the list of articles', async () => {
            let response = await withLogin(request(api).get('/articles'));
            expect(response).to.have.status(200);
        });
    });

    describe('list from personalization', () => {
        after(function () {
            nock.cleanAll();
        });

        it('should return the list of articles', async () => {
            nock(config.stream.baseUrl)
                .get(/winds_article_recommendations/)
                .reply(200, { results: [{foreign_id:`article:${article.id}`}] });

            const response = await withLogin(
                request(api).get('/articles').query({
                    type: 'recommended',
                })
            );
            expect(response).to.have.status(200);
            expect(response.body.length).to.be.at.least(1);
            expect(response.body[0].url).to.eq(article.url);
        });
    });
});

持续集成

有很多可用的持续集成服务,但我们钟爱 Travis CI,因为他们和我们一样喜爱开源环境。考虑到 Winds 是开源的,它再合适不过了。

我们的集成非常简单 —— 我们用 [.travis.yml] 文件设置环境,通过简单的 npm 命令进行测试。测试覆盖率反馈给 GitHub,在 GitHub 上我们将清楚地看出我们最新的代码或者 PR 是不是通过了测试。GitHub 集成很棒,因为它可以自动查询 Travis CI 获取结果。以下是一个在 GitHub 上看到 (经过了测试的) PR 的简单截图:

除了 Travis CI,我们还用到了叫做 CodeCov 的工具。CodeCov 和 [Istanbul] 很像,但它是个可视化的工具,方便我们查看代码覆盖率、文件变动、行数变化,还有其他各种小玩意儿。尽管不用 CodeCov 也可以可视化数据,但把所有东西囊括在一个地方也很不错。

我们学到了什么

在开发我们的测试套件的整个过程中,我们学到了很多东西。开发时没有所谓“正确”的方法,我们决定开始创造自己的测试流程,通过理清楚可用的库,找到那些足够有用的东西添加到我们的工具箱中。

最终我们学到的是,在 Node.js 中进行测试不是听上去那么简单。还好,随着 Node.js 持续完善,社区将会聚集力量,构建一个坚固稳健的库,可以用“正确”的方式处理所有和测试相关的东西。

但在那时到来之前,我们还会接着用自己的测试套件,它开源在 Winds 的 GitHub 仓库

局限

创建配置没有简单的方法

有的框架和语言,就如 Python 中的 Django,有简单的方式来创建配置。比如,你可以使用下面这些 Django 命令,把数据导出到文件中来自动化配置的创建过程:

以下命令会把整个数据库导出到 db.json 文件中:

./manage.py dumpdata > db.json

以下命令仅导出 django 中 admin.logentry 表里的内容:

./manage.py dumpdata admin.logentry > logentry.json

以下命令会导出 auth.user 表中的内容:

./manage.py dumpdata auth.user > user.json

Node.js 里面没有创建配置的简单方式。我们最后做的事情是用 MongoDB Compass 工具导出数据到 JSON 中。这生成了不错的配置,如下图(但是,这是个乏味的过程,肯定会出错):

使用 Babel,模拟模块和 Mocha 测试执行平台时,模块加载不直观

为了支持多种 node 版本,和获取 JavaScript 标准的最新附件,我们使用 Babel 把 ES6 代码转换成 ES5。Node.js 模块系统基于 CommonJS 标准,而 ES6 模块系统中有不同的语义。

Babel 在 Node.js 模块系统的顶层模拟 ES6 模块语义,但由于我们要使用 mock-require 来介入模块的加载,所以我们经历了罕见的怪异的模块加载过程,这看上去很不直观,而且能导致在整个代码中,导入的、初始化的和使用的模块有不同的版本。这使测试时的模拟过程和全局状态管理复杂化了。

在使用 ES6 模块时声明的函数,模块内部的函数,都无法模拟

当一个模块导出多个函数,其中一个函数调用了其他的函数,就不可能模拟使用在模块内部的函数。原因在于当你引用一个 ES6 模块时,你得到的引用集合和模块内部的是不同的。任何重新绑定引用,将其指向新值的尝试都无法真正影响模块内部的函数,内部函数仍然使用的是原始的函数。

最后的思考

测试 Node.js 应用是复杂的过程,因为它的生态系统总在发展。掌握最新和最好的工具很重要,这样你就不会掉队了。

如今有很多方式获取 JavaScript 相关的新闻,导致与时俱进很难。关注邮件新闻刊物如 JavaScript WeeklyNode Weekly 是良好的开始。还有,关注一些 reddit 子模块如 /r/node 也不错。如果你喜欢了解最新的趋势,State of JS 在测试领域帮助开发者可视化趋势方面就做的很好。

最后,这里是一些我喜欢的博客,我经常在这上面发文章:

觉得我遗漏了某些重要的东西?在评论区或者 Twitter @NickParsons 让我知道。

还有,如果你想要了解 Stream,我们的网站上有很棒的 5 分钟教程。点 这里 进行查看。


作者简介:

Nick Parsons

Dreamer. Doer. Engineer. Developer Evangelist https://getstream.io.


via: https://hackernoon.com/testing-node-js-in-2018-10a04dd77391

作者:Nick Parsons 译者:BriFuture 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

学习如何在 Linux 终端使用命令行工具 speedtest 测试网速,或者仅用一条 python 命令立刻获得网速的测试结果。

在Linux终端测试网速

我们都会在连接到一个新的网络或者 WIFI 的时候去测试网络带宽。 为什么不用我们自己的服务器!下面将会教你如何在 Linux 终端测试网速。

我们多数都会使用 Ookla 的 Speedtest 来测试网速。 这在桌面上是很简单的操作,访问他们的网站点击“Go”浏览即可。它将使用最近的服务器来扫描你的本地主机来测试网速。 如果你使用的是移动设备,他们有对应的移动端 APP。但如果你使用的是只有命令行终端,界面的则会有些不同。下面让我们一起看看如何在Linux的终端来测试网速。

如果你只是想偶尔的做一次网速测试而不想去下载测试工具,那么请往下看如何使用命令完成测试。

第一步:下载网速测试命令行工具。

首先,你需要从 GitHub 上下载 speedtest 命令行工具。现在,它也被包含在许多其它的 Linux 仓库中,如果已经在你的库中,你可以直接在你的 Linux 发行版上进行安装

让我们继续下载和安装过程,安装的 git 包取决于你的 Linux 发行版。然后按照下面的方法来克隆 Github speedtest 存储库

[root@kerneltalks ~]# git clone https://github.com/sivel/speedtest-cli.git
Cloning into 'speedtest-cli'...
remote: Counting objects: 913, done.
remote: Total 913 (delta 0), reused 0 (delta 0), pack-reused 913
Receiving objects: 100% (913/913), 251.31 KiB | 143.00 KiB/s, done.
Resolving deltas: 100% (518/518), done.

它将会被克隆到你当前的工作目录,新的名为 speedtest-cli 的目录将会被创建,你将在新的目录下看到如下的文件。

[root@kerneltalks ~]# cd speedtest-cli
[root@kerneltalks speedtest-cli]# ll
total 96
-rw-r--r--. 1 root root  1671 Oct  7 16:55 CONTRIBUTING.md
-rw-r--r--. 1 root root 11358 Oct  7 16:55 LICENSE
-rw-r--r--. 1 root root    35 Oct  7 16:55 MANIFEST.in
-rw-r--r--. 1 root root  5215 Oct  7 16:55 README.rst
-rw-r--r--. 1 root root    20 Oct  7 16:55 setup.cfg
-rw-r--r--. 1 root root  3196 Oct  7 16:55 setup.py
-rw-r--r--. 1 root root  2385 Oct  7 16:55 speedtest-cli.1
-rw-r--r--. 1 root root  1200 Oct  7 16:55 speedtest_cli.py
-rwxr-xr-x. 1 root root 47228 Oct  7 16:55 speedtest.py
-rw-r--r--. 1 root root   333 Oct  7 16:55 tox.ini

名为 speedtest.py 的 Python 脚本文件就是用来测试网速的。

你可以将这个脚本链接到 /usr/bin 下,以便这台机器上的所有用户都能使用。或者你可以为这个脚本创建一个命令别名,这样就能让所有用户很容易使用它。

运行 Python 脚本

现在,直接运行这个脚本,不需要添加任何参数,它将会搜寻最近的服务器来测试你的网速。

[root@kerneltalks speedtest-cli]# python speedtest.py
Retrieving speedtest.net configuration...
Testing from Amazon (35.154.184.126)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Spectra (Mumbai) [1.15 km]: 8.174 ms
Testing download speed................................................................................
Download: 548.13 Mbit/s
Testing upload speed................................................................................................
Upload: 323.95 Mbit/s

Oh! 不要被这个网速惊讶道。我在 AWE EX2 的服务器上。那是亚马逊数据中心的网速!

这个脚本可以添加有不同的选项。

下面的几个选项对这个脚本可能会很有用处:

要搜寻你附近的网路测试服务器,使用 --listgrep 加上地名来列出所有附近的服务器。

[root@kerneltalks speedtest-cli]#  python speedtest.py --list | grep -i mumbai
 2827) Bharti Airtel Ltd (Mumbai, India) [1.15 km]
 8978) Spectra (Mumbai, India) [1.15 km]
 4310) Hathway Cable and Datacom Ltd (Mumbai, India) [1.15 km]
 3315) Joister Broadband (Mumbai, India) [1.15 km]
 1718) Vodafone India (Mumbai, India) [1.15 km]
 6454) YOU Broadband India Pvt Ltd. (Mumbai, India) [1.15 km]
 9764) Railtel Corporation of india Ltd (Mumbai, India) [1.15 km]
 9584) Sheng Li Telecom (Mumbai, India) [1.15 km]
 7605) Idea Cellular Ltd. (Mumbai, India) [1.15 km]
 8122) Sify Technologies Ltd (Mumbai, India) [1.15 km]
 9049) I-ON (Mumbai, India) [1.15 km]
 6403) YOU Broadband India Pvt Ltd., Mumbai (Mumbai, India) [1.15 km]

然后你就能从搜寻结果中看到,第一列是服务器识别号,紧接着是公司的名称和所在地,最后是离你的距离。

如果要使用指定的服务器来测试网速,后面跟上 --server 加上服务器的识别号。

[root@kerneltalks speedtest-cli]# python speedtest.py --server 2827
Retrieving speedtest.net configuration...
Testing from Amazon (35.154.184.126)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Bharti Airtel Ltd (Mumbai) [1.15 km]: 13.234 ms
Testing download speed................................................................................
Download: 93.47 Mbit/s
Testing upload speed................................................................................................
Upload: 69.25 Mbit/s

如果想得到你的测试结果的分享链接,使用 --share,你将会得到测试结果的链接。

[root@kerneltalks speedtest-cli]# python speedtest.py --share
Retrieving speedtest.net configuration...
Testing from Amazon (35.154.184.126)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Spectra (Mumbai) [1.15 km]: 7.471 ms
Testing download speed................................................................................
Download: 621.00 Mbit/s
Testing upload speed................................................................................................
Upload: 367.37 Mbit/s
Share results: http://www.speedtest.net/result/6687428141.png

输出中的最后一行就是你的测试结果的链接。下载下来的图片内容如下 :

这就是全部的过程!如果你不想了解这些技术细节,你也可以使用如下的一行命令迅速测出你的网速。

要想在终端使用一条命令测试网速。

我们将使用 curl 工具来在线抓取上面使用的 Python 脚本然后直接用 Python 执行脚本。

[root@kerneltalks ~]# curl -s https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py | python -

上面的脚本将会运行脚本输出结果到屏幕上。

[root@kerneltalks speedtest-cli]# curl -s https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py | python -
Retrieving speedtest.net configuration...
Testing from Amazon (35.154.184.126)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Spectra (Mumbai) [1.15 km]: 12.599 ms
Testing download speed................................................................................
Download: 670.88 Mbit/s
Testing upload speed................................................................................................
Upload: 355.84 Mbit/s

这是在 RHEL 7 上执行的结果,在 Ubuntu、Debian、Fedora 或者 CentOS 上一样可以执行。


via: https://kerneltalks.com/tips-tricks/how-to-test-internet-speed-in-linux-terminal/

作者:Shrikant Lavhate 译者:FelixYFZ 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你知道你的硬盘在 Linux 下传输有多快吗?不打开电脑的机箱或者机柜,你知道它运行在 SATA I (150 MB/s) 、 SATA II (300 MB/s) 还是 SATA III (6.0Gb/s) 呢?

你能够使用 hdparmdd 命令来检测你的硬盘速度。它为各种硬盘的 ioctls 提供了命令行界面,这是由 Linux 系统的 ATA / IDE / SATA 设备驱动程序子系统所支持的。有些选项只能用最新的内核才能正常工作(请确保安装了最新的内核)。我也推荐使用最新的内核源代码的包含头文件来编译 hdparm 命令。

如何使用 hdparm 命令来检测硬盘的传输速度

以 root 管理员权限登录并执行命令:

$ sudo hdparm -tT /dev/sda

或者,

$ sudo hdparm -tT /dev/hda

输出:

/dev/sda:
 Timing cached reads: 7864 MB in 2.00 seconds = 3935.41 MB/sec
 Timing buffered disk reads: 204 MB in 3.00 seconds = 67.98 MB/sec

为了检测更精准,这个操作应该重复2-3次 。这显示了无需访问磁盘,直接从 Linux 缓冲区缓存中读取的速度。这个测量实际上是被测系统的处理器、高速缓存和存储器的吞吐量的指标。这是一个 for 循环的例子,连续运行测试 3 次:

for i in 1 2 3; do hdparm -tT /dev/hda; done

这里,

  • -t :执行设备读取时序
  • -T :执行缓存读取时间
  • /dev/sda :硬盘设备文件

找出 SATA 硬盘的连接速度 ,请输入:

sudo hdparm -I /dev/sda | grep -i speed

输出:

     *  Gen1 signaling speed (1.5Gb/s)
     *  Gen2 signaling speed (3.0Gb/s)
     *  Gen3 signaling speed (6.0Gb/s)

以上输出表明我的硬盘可以使用 1.5Gb/s、3.0Gb/s 或 6.0Gb/s 的速度。请注意,您的 BIOS/主板必须支持 SATA-II/III 才行:

$ dmesg | grep -i sata | grep 'link up'

Linux Check IDE SATA SSD Hard Disk Transfer Speed

dd 命令

你使用 dd 命令也可以获取到相应的速度信息:

dd if=/dev/zero of=/tmp/output.img bs=8k count=256k
rm /tmp/output.img

输出:

262144+0 records in
262144+0 records out
2147483648 bytes (2.1 GB) copied, 23.6472 seconds, `90.8 MB/s`

下面是 推荐的 dd 命令参数

dd if=/dev/input.file  of=/path/to/output.file  bs=block-size  count=number-of-blocks  oflag=dsync

## GNU dd syntax ##
dd if=/dev/zero of=/tmp/test1.img bs=1G count=1 oflag=dsync

## OR alternate syntax for GNU/dd ##
dd if=/dev/zero of=/tmp/testALT.img bs=1G count=1 conv=fdatasync

这是上面命令的第三个命令的输出结果:

1+0 records in
1+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 4.23889 s, 253 MB/s

“磁盘与存储” - GUI 工具

您还可以使用位于“系统>管理>磁盘实用程序”菜单中的磁盘实用程序。请注意,在最新版本的 Gnome 中,它简称为“磁盘”。

如何使用 Linux 上的“磁盘”测试我的硬盘的性能?

要测试硬盘的速度:

  1. 从“活动概览”中打开“磁盘”(按键盘上的 super 键并键入“disks”)
  2. 从“左侧窗格”的列表中选择“磁盘”
  3. 选择菜单按钮并从菜单中选择“测试磁盘性能……”
  4. 单击“开始性能测试……”并根据需要调整传输速率和访问时间参数。
  5. 选择“开始性能测试”来测试从磁盘读取数据的速度。需要管理权限请输入密码。

以上操作的快速视频演示:

https://www.cyberciti.biz/tips/wp-content/uploads/2007/10/disks-performance.mp4

只读 Benchmark (安全模式下)

然后,选择 > 只读:

 title=

上述选项不会销毁任何数据。

读写的 Benchmark(所有数据将丢失,所以要小心)

访问“系统>管理>磁盘实用程序菜单>单击性能测试>单击开始读/写性能测试按钮:

 title=

作者

作者是 nixCraft 的创造者,是经验丰富的系统管理员,也是 Linux 操作系统/ Unix shell 脚本的培训师。他曾与全球客户以及 IT,教育,国防和空间研究以及非营利部门等多个行业合作。在Twitter,Facebook和Google+上关注他。


via: https://www.cyberciti.biz/tips/how-fast-is-linux-sata-hard-disk.html

作者:Vivek Gite 译者:MonkeyDEcho 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Linux 4.14 发布候选第五版已经出来。Linus Torvalds 说:“可以去测试了。”

linus-toravlds-linuxcon-toronto.jpg

随着宣布推出 Linux 内核 4.14 的第五个候选版本,Linus Torvalds 表示 模糊测试 fuzzing 正产生一系列稳定的安全更新。

模糊测试通过产生随机代码来引发错误来对系统进行压力测试,从而有助于识别潜在的安全漏洞。模糊测试可以帮助软件开发人员在向用户发布软件之前捕获错误。

Google 使用各种模糊测试工具来查找它及其它供应商软件中的错误。微软推出了 Project Springfield 模糊测试服务,它能让企业客户测试自己的软件。

正如 Torvalds 指出的那样,Linux 内核开发人员从一开始就一直在使用模糊测试流程,例如 1991 年发布的工具 “crashme”,它在近 20 年后被 Google 安全研究员 Tavis Ormandy 用来测试在虚拟机中处理不受信任的数据时,宿主机是否受到良好保护。

Torvalds :“另外值得一提的是人们做了多少随机化模糊测试,而且这正在发现东西。”

“我们一直在做模糊测试(谁还记得只是生成随机代码,并跳转过去的老 “crashme” 程序?我们过去很早就这样做),人们在驱动子系统等方面做了一些很好的针对性模糊测试,而且已经有了各种各样的修复(不仅仅是上周的这些)。很高兴可以看到。”

Torvalds 提到,到目前为止,4.14 的发展“比预想的要麻烦一些”,但现在已经好了,并且在这个版本已经跑通了一些针对 x86 系统以及 AMD 芯片系统的修复。还有几个驱动程序、核心内核组件和工具的更新。

如前所述,Linux 4.14 是 2017 年的长期稳定版本,迄今为止,它引入了核心内存管理功能、设备驱动程序更新以及文档、架构、文件系统、网络和工具的修改。


via: http://www.zdnet.com/article/linus-torvalds-says-targeted-fuzzing-is-improving-linux-security/

作者:Liam Tung 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Flashback 用于测试目的来模拟 HTTP 和 HTTPS 资源,如 Web 服务和 REST API。

在 LinkedIn,我们经常开发需要与第三方网站交互的 Web 应用程序。我们还采用自动测试,以确保我们的软件在发布到生产环境之前的质量。然而,测试只是在它可靠时才有用。

考虑到这一点,有外部依赖关系的测试是有很大的问题的,例如在第三方网站上。这些外部网站可能会没有通知地发生改变、遭受停机,或者由于互联网的不可靠性暂时无法访问。

如果我们的一个测试依赖于能够与第三方网站通信,那么任何故障的原因都很难确定。失败可能是因为 LinkedIn 的内部变更、第三方网站的维护人员进行的外部变更,或网络基础设施的问题。你可以想像,与第三方网站的交互可能会有很多失败的原因,因此你可能想要知道,我将如何处理这个问题?

好消息是有许多互联网模拟工具可以帮助。其中一个是 Betamax。它通过拦截 Web 应用程序发起的 HTTP 连接,之后进行重放的方式来工作。对于测试,Betamax 可以用以前记录的响应替换 HTTP 上的任何交互,它可以非常可靠地提供这个服务。

最初,我们选择在 LinkedIn 的自动化测试中使用 Betamax。它工作得很好,但我们遇到了一些问题:

  • 出于安全考虑,我们的测试环境没有接入互联网。然而,与大多数代理一样,Betamax 需要 Internet 连接才能正常运行。
  • 我们有许多需要使用身份验证协议的情况,例如 OAuth 和 OpenId。其中一些协议需要通过 HTTP 进行复杂的交互。为了模拟它们,我们需要一个复杂的模型来捕获和重放请求。

为了应对这些挑战,我们决定基于 Betamax 的思路,构建我们自己的互联网模拟工具,名为 Flashback。我们也很自豪地宣布 Flashback 现在是开源的。

什么是 Flashback?

Flashback 用于测试目的来模拟 HTTP 和 HTTPS 资源,如 Web 服务和 REST API。它记录 HTTP/HTTPS 请求并重放以前记录的 HTTP 事务 - 我们称之为“ 场景 scene ”,这样就不需要连接到 Internet 才能完成测试。

Flashback 也可以根据请求的部分匹配重放场景。它使用的是“匹配规则”。匹配规则将传入请求与先前记录的请求相关联,然后将其用于生成响应。例如,以下代码片段实现了一个基本匹配规则,其中测试方法“匹配”此 URL的传入请求。

HTTP 请求通常包含 URL、方法、标头和正文。Flashback 允许为这些组件的任意组合定义匹配规则。Flashback 还允许用户向 URL 查询参数,标头和正文添加白名单或黑名单标签。

例如,在 OAuth 授权流程中,请求查询参数可能如下所示:

oauth_consumer_key="jskdjfljsdklfjlsjdfs",
oauth_nonce="ajskldfjalksjdflkajsdlfjasldfja;lsdkj",
oauth_signature="asdfjaklsdjflasjdflkajsdklf",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="asdjfkasjdlfajsdklfjalsdjfalksdjflajsdlfa",
oauth_version="1.0"

这些值许多将随着每个请求而改变,因为 OAuth 要求客户端每次为 oauth_nonce 生成一个新值。在我们的测试中,我们需要验证 oauth_consumer_keyoauth_signature_methodoauth_version 的值,同时确保 oauth_nonceoauth_signatureoauth_timestampoauth_token 存在于请求中。Flashback 使我们有能力创建我们自己的匹配规则来实现这一目标。此功能允许我们测试随时间变化的数据、签名、令牌等的请求,而客户端没有任何更改。

这种灵活的匹配和在不连接互联网的情况下运行的功能是 Flashback 与其他模拟解决方案不同的特性。其他一些显著特点包括:

  • Flashback 是一种跨平台和跨语言解决方案,能够测试 JVM(Java虚拟机)和非 JVM(C++、Python 等)应用程序。
  • Flashback 可以随时生成 SSL/TLS 证书,以模拟 HTTPS 请求的安全通道。

如何记录 HTTP 事务

使用 Flashback 记录 HTTP 事务以便稍后重放是一个比较简单的过程。在我们深入了解流程之前,我们首先列出一些术语:

  • Scene :场景存储以前记录的 HTTP 事务 (以 JSON 格式),它可以在以后重放。例如,这里是一个Flashback 场景示例。
  • Root Path :根路径是包含 Flashback 场景数据的目录的文件路径。
  • Scene Name :场景名称是给定场景的名称。
  • Scene Mode :场景模式是使用场景的模式, 即“录制”或“重放”。
  • Match Rule :匹配规则确定传入的客户端请求是否与给定场景的内容匹配的规则。
  • Flashback Proxy :Flashback 代理是一个 HTTP 代理,共有录制和重放两种操作模式。
  • HostPort :代理主机和端口。

为了录制场景,你必须向目的地址发出真实的外部请求,然后 HTTPS 请求和响应将使用你指定的匹配规则存储在场景中。在录制时,Flashback 的行为与典型的 MITM(中间人)代理完全相同 - 只有在重放模式下,连接流和数据流仅限于客户端和代理之间。

要实际看下 Flashback,让我们创建一个场景,通过执行以下操作捕获与 example.org 的交互:

1、 取回 Flashback 的源码:

git clone https://github.com/linkedin/flashback.git

2、 启动 Flashback 管理服务器:

./startAdminServer.sh -port 1234

3、 注意上面的 Flashback 将在本地端口 5555 上启动录制模式。匹配规则需要完全匹配(匹配 HTTP 正文、标题和 URL)。场景将存储在 /tmp/test1 下。

4、 Flashback 现在可以记录了,所以用它来代理对 example.org 的请求:

curl http://www.example.org -x localhost:5555 -X GET

5、 Flashback 可以(可选)在一个记录中记录多个请求。要完成录制,关闭 Flashback

6、 要验证已记录的内容,我们可以在输出目录(/tmp/test1)中查看场景的内容。它应该包含以下内容

在 Java 代码中使用 Flashback也很容易。

如何重放 HTTP 事务

要重放先前存储的场景,请使用与录制时使用的相同的基本设置。唯一的区别是将“场景模式”设置为上述步骤 3 中的“播放”

验证响应来自场景而不是外部源的一种方法,是在你执行步骤 1 到 6 时临时禁用 Internet 连接。另一种方法是修改场景文件,看看响应是否与文件中的相同。

这是 Java 中的一个例子

如何记录并重播 HTTPS 事务

使用 Flashback 记录并重放 HTTPS 事务的过程非常类似于 HTTP 事务的过程。但是,需要特别注意用于 HTTPS SSL 组件的安全证书。为了使 Flashback 作为 MITM 代理,必须创建证书颁发机构(CA)证书。在客户端和 Flashback 之间创建安全通道时将使用此证书,并允许 Flashback 检查其代理的 HTTPS 请求中的数据。然后将此证书存储为受信任的源,以便客户端在进行调用时能够对 Flashback 进行身份验证。有关如何创建证书的说明,有很多类似这样的资源是非常有帮助的。大多数公司都有自己的管理和获取证书的内部策略 - 请务必用你们自己的方法。

这里值得一提的是,Flashback 仅用于测试目的。你可以随时随地将 Flashback 与你的服务集成在一起,但需要注意的是,Flashback 的记录功能将需要存储所有的数据,然后在重放模式下使用它。我们建议你特别注意确保不会无意中记录或存储敏感成员数据。任何可能违反贵公司数据保护或隐私政策的行为都是你的责任。

一旦涉及安全证书,HTTP 和 HTTPS 之间在记录设置方面的唯一区别是添加了一些其他参数。

  • RootCertificateInputStream: 表示 CA 证书文件路径或流。
  • RootCertificatePassphrase: 为 CA 证书创建的密码。
  • CertificateAuthority: CA 证书的属性

查看 Flashback 中用于记录 HTTPS 事务的代码,它包括上述条目。

用 Flashback 重放 HTTPS 事务的过程与录制相同。唯一的区别是场景模式设置为“播放”。这在此代码中演示。

支持动态修改

为了测试灵活性,Flashback 允许你动态地更改场景和匹配规则。动态更改场景允许使用不同的响应(如 successtime_outrate_limit 等)测试相同的请求。场景更改仅适用于我们已经 POST 更新外部资源的场景。以下图为例。

 title=

能够动态更改匹配规则可以使我们测试复杂的场景。例如,我们有一个使用情况,要求我们测试 Twitter 的公共和私有资源的 HTTP 调用。对于公共资源,HTTP 请求是不变的,所以我们可以使用 “MatchAll” 规则。然而,对于私人资源,我们需要使用 OAuth 消费者密码和 OAuth 访问令牌来签名请求。这些请求包含大量具有不可预测值的参数,因此静态 MatchAll 规则将无法正常工作。

使用案例

在 LinkedIn,Flashback 主要用于在集成测试中模拟不同的互联网提供商,如下图所示。第一张图展示了 LinkedIn 生产数据中心内的一个内部服务,通过代理层,与互联网提供商(如 Google)进行交互。我们想在测试环境中测试这个内部服务。

 title=

第二和第三张图表展示了我们如何在不同的环境中录制和重放场景。记录发生在我们的开发环境中,用户在代理启动的同一端口上启动 Flashback。从内部服务到提供商的所有外部请求将通过 Flashback 而不是我们的代理层。在必要场景得到记录后,我们可以将其部署到我们的测试环境中。

 title=

在测试环境(隔离并且没有 Internet 访问)中,Flashback 在与开发环境相同的端口上启动。所有 HTTP 请求仍然来自内部服务,但响应将来自 Flashback 而不是 Internet 提供商。

 title=

未来方向

我们希望将来可以支持非 HTTP 协议(如 FTP 或 JDBC),甚至可以让用户使用 MITM 代理框架来自行注入自己的定制协议。我们将继续改进 Flashback 设置 API,使其更容易支持非 Java 语言。

现在为一个开源项目

我们很幸运能够在 GTAC 2015 上发布 Flashback。在展会上,有几名观众询问是否将 Flashback 作为开源项目发布,以便他们可以将其用于自己的测试工作。

Google TechTalks:GATC 2015 - 模拟互联网

我们很高兴地宣布,Flashback 现在以 BSD 两句版许可证开源。要开始使用,请访问 Flashback GitHub 仓库

该文原始发表在LinkedIn 工程博客上。获得转载许可

致谢

Flashback 由 Shangshang FengYabin KangDan Vinegrad 创建,并受到 Betamax 启发。特别感谢 Hwansoo LeeEran LeshemKunal KandekarKeith DsouzaKang Wang 帮助审阅代码。同样感谢我们的管理层 - Byron MaYaz ShimizuYuliya AverbukhChristopher HazlettBrandon Duncan - 感谢他们在开发和开源 Flashback 中的支持。


作者简介:

Shangshang Feng - Shangshang 是 LinkedIn 纽约市办公室的高级软件工程师。在 LinkedIn 他从事了三年半的网关平台工作。在加入 LinkedIn 之前,他曾在 Thomson Reuters 和 ViewTrade 证券的基础设施团队工作。


via: https://opensource.com/article/17/4/flashback-internet-mocking-tool

作者: Shangshang Feng 译者:geekpi 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出