如何让测试变得有趣和容易 -欧洲杯足彩官网

0顶
0踩

如何让测试变得有趣和容易

2017-09-14 16:16 by 副主编 jihong10102006 评论(0) 有8454人浏览
引用
原文:
作者:maciek głowacki
译者:teixeira10

【译者注】本文中,作者讲述了如何利用在apirequest类来让测试变得有趣和容易,同时提供了大量的代码示例供读者阅读和参考。
以下为译文:

测试,你可能会喜欢它,你也可能讨厌它,但是你应该同意好的测试代码对你和你的团队是有用的,甚至将来可能对执行你项目的合作者都是有益的。测试可能不是你工作中最令人兴奋的部分,但它确实非常有用。在重构和创建新特性时,经过测试的代码会让你感到很安心。

还是,如果这些测试代码不是你写的呢?你确定这些涵盖了所有事情吗?它们真的测试了什么或者只是模拟了整个应用程序吗?所以你还得花时间确保现有的测试是有用的,并且写得很好。

如果在项目中没有测试规则,那么就应该用如下所说的方式。

创建一些规则

现在你可能想提出关于测试的规则。是的,那就这样做吧!

从现在开始,让我们编写良好的测试代码并实现100%的代码覆盖率。

然后将这个想法传递给团队的其他成员,也让他们执行起来。

但这会奏效吗?你可能会得到一堆“测试代码”,这些“测试代码”拼拼凑凑,这样就可以在工作量少的情况下获得高覆盖率。那么“好的测试代码例”部分呢?谁会知道这是什么意思呢。我打赌你也对这样的结果不满意。所以让我们做出一些改变吧!

但是你真的知道这种方法有什么问题吗?首先,它并没有使编写代码变得更快或更简单。实际上,它的情况恰恰相反——至少编写两倍代码。如果你让别人写测试代码,他们很可能会这么做,但你觉得他们会用心去做吗?

开发人员需要的是奖励,而不是惩罚

既然惩罚不是好方法,那就试试奖励吧。如果写测试代码能立即得到奖励呢?如果没有额外的工作,如何生成一个api文档呢?如果你问我,那我觉得这是很好的,而这个特殊的“奖励”正是开始写更好的测试代码所需要的。

(别误会我的意思,好的测试代码本身就很好,而且从长远来看,总会有回报的。然而,一些即时的满足感可以成为一种真正的提高效率的助推器,特别是当你做一些琐碎的事情时)

在这一点上,你必须做出选择。

你可以继续阅读,来发现测试可以变得多么有趣,或者你可以直接跳到一个示例应用程序(但是你可能会错过很多)。

那么你选择阅读了吗?非常好!那么,如果你有一石二鸟的想法,那就来看看怎样才能让编写测试代码变得更容易。既然已经使用了 rspec,那就让 rspec_api_documentation gem使事情变得更简单。根据说明将其添加到你的项目中,这样你就可以创建第一个测试工程:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    with_options scope: :post do
      parameter :title, 'title of a post. can be empty'
      parameter :body, 'main text of a post. must be longer than 10 letters', required: true
    end
    response_field :id, 'id of the created post'
    response_field :title, 'title of the created post'
    response_field :body, 'main text of the created post'
    header 'accept', 'application/json'
    header 'content-type', 'application/json'
    let(:title) { 'foo' }
    let(:body) { 'lorem ipsum dolor sit amet' }
    example_request 'creating a post' do
      explanation 'you can create a post by sending its body text and an optional title'
      expect(status).to eq 201
      response = json.parse(response_body)
      expect(response['title']).to eq title
      expect(response['body']).to eq body
    end
  end
end

来看看这段测试代码,你就能明白这个应用程序能做什么了。可以立即看到参数是什么,响应是什么,应该发送什么消息头。但在运行rakerake docs:generate之后,它会变得更好:生成并等待测试完成,同时你会得到以下的结果:

这是不是又快又容易呢?现在,如果想在这个文档中添加更多的例子,就必须继续为它编写测试代码。这可以覆盖所有3个情况:
  • 有效的请求
  • 无效的参数
  • 缺失的参数
现在,测试开始变得有用了。那我们是否遇到过中意外地中断了创建的帖子?不用担心——会有一个测试来负责这个问题。也许已经禁用了一些看起来没有必要的验证,但实际上不是这样的,由于需要文档来获取无效的参数,因此也会有一个测试。

刚刚解决了一个测试问题,所以现在它比以前更有趣了,并产生了一些即时可见的东西。但测试既不容易写更不容易写好。

测试的丑陋一面

我们有一个api允许创建帖子。如果用户可以选择在twitter或facebook上发布这些帖子,难道不是很好吗?听起来棒极了!但每次运行测试时,我们都不希望碰到第三方api,对吧?与此同时,检查是否会有一个请求会更好。

听起来像可以做的事情。我们将它添加到gemfile并按照指令安装。从现在起,不能在测试中与网络进行连接,同时必须明确地告诉webmock,我们将会提出一个特定的请求,并记住要用rspec来设置一个期望值:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as previously ...
before do
  @request = stub_request(:post, 'https://api.twitter.com/1.1/statuses/update.json')
               .with(body: { status: body })
               .to_return(status: 200, body: { id: 1 }.to_json)
end
example_request 'creating a post' do
  # ... same as previously ...
  expect(@request).to have_been_requested
end
end 
end 

这看起来不太糟,但这只是看一个人写的一个测试,如果让10个人写同样的测试,可能会得到10种不同的欧洲杯足彩官网的解决方案。如果有人想要快速地越过stubbing,甚至不检查发送的参数,那该怎么办呢?如果别人忘了检查是否发出了请求怎么办?有些东西可能会被破坏,没有人会知道,直到为时已晚。

似乎又回到了起点——必须确保其他人的测试按照预期的方式运行。但是,如何确保所有人都以同样的方式编写测试呢?

让测试更容易

问题是,编写糟糕的测试比编写好的测试要容易得多。当可以用更少的工作量来“让它变得更环保”的时候,这就是为什么人们会在意这些请求,并设定良好的期望结果。毕竟,它们将有一个passing测试和一个生成的文档。

必须在某种程度上超越懒惰的开发人员,并让编写好的测试代码比编写糟糕的测试代码更容易。如果能给他们一个不错的写测试的方法,而实际上却没有他们写测试的感觉呢?嗯,也许吧。但这是不可能的。

这里的想法是创建某种内部来描述测试用例,不希望它过于花哨——只是提取常见测试逻辑的简单方法。并且我们还希望是一些已经熟悉rspec的人,因为将围绕现有的语法来构建它。

提取公共逻辑听起来像是一个共享示例的任务。创建shared_examples_for_api_request并将其初始化,来描述端点:
  • 命名
  • 解释
  • 标题
  • 请求示例
它看起来是这样的:
shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example_request name do
    explanation explanation
# ... do some stuff here later ...
end 
end 

要使用这个,只需要调用:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    with_options scope: :post do
      parameter :title, 'title of a post. can be empty'
      parameter :body, 'main text of a post. must be longer than 10 letters', required: true
    end
response_field :id, 'id of the created post'
response_field :title, 'title of the created post'
response_field :body, 'main text of the created post'
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

现在可以开始研究最有趣的部分了。我们自己的dsl。

自己动手

我们的目标是创建一个对象,用于自动设置stub和测试的期望值。应该从一个新的类开始:
class apirequest
  def initialize
  end
end

require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as before ...
subject do
  apirequest.new
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

现在共享示例中有了rspec,但它还没有真正起作用。首先要检查的是请求是否成功。你知道如何在apirequest上通过调用.success或.failure来指定它呢?
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as before ...
subject do
  apirequest.new.success
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

这些只是apirequest的方法,它可以改变它的内部状态来指定预期的响应代码。它们应该返回正在处理的对象,这样就可以在以后处理更多的东西:
class apirequest
  attr_reader :status
  def initialize
  end
  def success(code = 200)
    @status = code
    self
  end
  def failure(code = 422)
    @status = code
    self
  end
end

shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example name do
    explanation explanation
    do_request
expect(status).to eq(subject.status)
end 
end 

它现在开始变得有用了,但是仅仅检查状态代码是不够的,也需要检查一下响应代码。
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as before ...
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
subject do
  apirequest.test.success(201)
            .response(:id, title: title, body: body)
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

现在,是实施的时候了。使用.test对初始化对象进行测试和.new一样简单。但在使用.response的时候必须记住,希望它接受关键字和键值对,而且必须把它们分开存储,因为它们将以不同的方式进行测试:
class apirequest
  attr_reader :status,
              :response_keys,
              :response_spec
  def initialize
    @response_keys = []
    @response_spec = {}
  end
  def self.test
    new
  end
  def response(*extra_keys, **extra_spec)
    @response_keys  = extra_keys.map(&:to_sym)
    @response_spec.merge!(extra_spec)
    self
  end
  # ... other methods written previously ...
end

shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example name do
    explanation explanation
    do_request
expect(status).to eq(subject.status)
res = json.parse(response_body).deep_symbolize_keys
expect(res).to include(*subject.response_keys)
subject.response_spec.each do |k, v|
  expect(res[k]).to eq(v), "expected #{k} to equal '#{v}', but got '#{res[k]}'"
end
end 
end 

现在已经有了一些可靠的基础来测试请求。但在请求之后检查某个对象的状态通常是很必要的。然而,这可以与测试不同,因此不能将其描述为dsl的一部分。但是可以通过一些这样的定制测试来进行:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as before ...
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
subject do
  apirequest.test.success(201)
            .response(:id, title: title, body: body)
            .and do
    expect(post.count).to eq(1)
    post = post.last
    expect(post.title).to eq(title)
    expect(post.body).to eq(body)
  end
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

在这里传递一个块,你可能会猜到实现的样子:
class apirequest
  attr_reader :status,
              :response_keys,
              :response_spec,
              :specs
  def initialize
    @response_keys = []
    @response_spec = {}
    @specs = proc {}
  end
  def and(&specs)
    @specs = specs
    self
  end
  # ... the rest stays unchanged ...
end

shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example name do
    res = json.parse(response_body).deep_symbolize_keys
# ... we only add this line ...
instance_exec(res, &subject.specs)
end 
end 

dsl已经开始看起来相当不错了,甚至还没有实现它的关键特性。现在为请求stubbing做准备,因为它会变得更加困难。

让我们掷重炮

在深入到stubbing api调用之前,还有一件事应该看看。假设除了在twitter和facebook上发布消息之外,应用程序还发送了一封电子邮件(不知道是发给谁,可能是cia)。这听起来像是在验收测试中应该处理的事情。

假设要检查在创建新post之后是否发送通知电子邮件,我建议这样做:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... params and stuff ...
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
subject do
  apirequest.test.success(201)
            .response(:id, title: title, body: body)
            .email.to('notify@cia.gov').with(subject: 'new post published', body: body)
            .and do
    # ... same as before ...
  end
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

如果.to 和 .with不属于apirequest本身,.email应该创建其他类的对象,这与正在处理的请求绑定在一起。可以把它看作是一种apirequest的方法,来描述mail。听起来合理吗?来看看代码:
class apirequest
  attr_reader :status,
              :response_keys,
              :response_spec,
              :specs,
              :messages
  def initialize
    @response_keys = []
    @response_spec = {}
    @specs = proc {}
    @messages = []
  end
  def email
    @messages 

收尾工作

用于生成文档的gem允许在单个示例中生成一些请求,但是我们的实现仅限于其中一个。为了解决这个问题,可以接受一个api请求数组,而不是一个实例。为了保持与现有代码的兼容性,将把主题包装在数组中(如果已经是数组,它将不会做任何事情):
shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example name do
    explanation explanation
array.wrap(subject).each do |request|
  actionmailer::base.deliveries = []
  # ... previous test stuff goes here ...
  # ... just remember to use request instead of subject ...
  request.stubs.each do |stub|
    expect(stub.data).to have_been_requested.at_least_once
    webmock::stubregistry.instance.remove_request_stub(stub.data)
  end
end
end 
end 

可以在一个例子中执行很多请求,但是它还不能很好地使用。所以必须添加一种方法来轻松地覆盖一些参数。让我们为apirequest类添加最后一个方法:
class apirequest
  attr_reader :status,
              :response_keys,
              :response_spec,
              :specs,
              :messages,
              :stubs,
              :params
  def initialize
    @response_keys = []
    @response_spec = {}
    @specs = proc {}
    @messages = []
    @stubs = []
    @params = {}
  end
  def with(params)
    @params = params
    self
  end
  # ... the rest stays the same ...
end

shared_examples 'api_requests' do |name, explanation|
  header 'accept', 'application/json'
  header 'content-type', 'application/json'
  example name do
    explanation explanation
array.wrap(subject).each do |request|
  # ... other stuff happening here ...
  do_request(request.params)
  # ... and here ...
end
end 
end 

有了这些,现在可以在每个示例中执行多个请求:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    # ... same as before ...
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
subject do
  requests = []
  requests << # ... previous "success" subject here
  requests << apirequest.test.failure
                        .with(post: { body: 'too short' })
                        .response(body: ['002'])
                        .and do
    expect(post.count).to eq(1)
  end
  requests
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end
end 

最好的方法是,在文档中立即有它们(注意前面的长图)

终于完成了。刚刚创建了一个很容易使用的dsl,它让我们能够改变这个长期且容易出错的测试:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    with_options scope: :post do
      parameter :title, 'title of a post. can be empty'
      parameter :body, 'main text of a post. must be longer than 10 letters', required: true
    end
response_field :id, 'id of the created post'
response_field :title, 'title of the created post'
response_field :body, 'main text of the created post'
header 'accept', 'application/json'
header 'content-type', 'application/json'
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
before do
  @twitter_request = stub_request(:post, 'https://api.twitter.com/1.1/statuses/update.json')
                       .with(body: { status: body })
                       .to_return(status: 200, body: { id: 1 }.to_json)
  @facebook_request = stub_request(:post, 'https://graph.facebook.com/me/feed')
                        .with(body: hash_including(:access_token,
                                                   :appsecret_proof,
                                                   message: body))
                        .to_return(status: 200, body: { id: 1 }.to_json)
end
example 'creating a post' do
  explanation 'you can create a post by sending its body text and an optional title'
  do_request
  expect(status).to eq 201
  response = json.parse(response_body)
  expect(response.keys).to include 'id'
  expect(response['title']).to eq title
  expect(response['body']).to eq body
  expect(post.count).to eq(1)
  post = post.last
  expect(post.title).to eq(title)
  expect(post.body).to eq(body)
  expect(actionmailer::base.deliveries.count).to eq(1)
  email = actionmailer::base.deliveries.last
  expect(email.to).to include 'notify@cia.gov'
  expect(email.subject).to include 'new post published'
  expect(email.body).to include body
  expect(@twitter_request).to have_been_requested
  expect(@facebook_request).to have_been_requested
end
end 
end 

更易读和更容易使用的形式:
require 'acceptance_helper'
resource 'posts' do
  explanation 'posts are entities holding some text information.
               they can be created and seen by anyone'
  post '/posts' do
    with_options scope: :post do
      parameter :title, 'title of a post. can be empty'
      parameter :body, 'main text of a post. must be longer than 10 letters', required: true
    end
response_field :id, 'id of the created post'
response_field :title, 'title of the created post'
response_field :body, 'main text of the created post'
let(:title) { 'foo' }
let(:body) { 'lorem ipsum dolor sit amet' }
subject do
  apirequest.test.success(201)
            .response(:id, title: title, body: body)
            .email.to('notify@cia.gov').with(
              subject: 'new post published',
              body: body)
            .request.twitter.with(status: body).status_update.success
            .request.facebook.with(message: body).put_wall_post.success
            .and do
    expect(post.count).to eq(1)
    post = post.last
    expect(post.title).to eq(title)
    expect(post.body).to eq(body)
  end
end
include_examples 'api_requests',
                 'creating a post',
                 'you can create a post by sending its body text and an optional title'
end 
end 

现在使用apirequest比手工编写测试更快,这样就可以很容易地说服团队的其他人使用它来进行验收测试。因此,现在可以得到值得信任的测试,以及api文档。目标实现了!

最后的话

在apirequest类的帮助下,可以在几分钟内编写新的端点测试,可以很容易地指定业务需求,因此进一步的开发也变得更容易。但请记住,这些只是验收测试。你仍然应该对代码进行单元测试,以捕获任何实现的错误。

为了向你展示如何在实际应用程序中使用这个方法,我已经准备了一个github存储库,它具有一个完整的非常基本的用例。自己试一下:


就这篇文章而言:让测试易于编写,同时保持高水平的可用性,当然这里还有很多事情可以做。例如,可以部分地生成基于stub的端点描述。或者,可以想出一种方法,提取一些共同的逻辑,然后进行分享。
  • 大小: 100.1 kb
  • 大小: 209.5 kb
  • 大小: 128.5 kb
  • 大小: 292.3 kb
0
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • cuked 是一个自动化测试框架,结合了 cucumberjs、phantomjs、saucelabs、webdriver.io 和 chai 的强大功能,使编写测试变得有趣而轻松。 cuked 是单体测试框架的替代品,它使您陷入专有抽象和 api 中。 cuked 是...

  • 随着工作经验的积累,对书中内容的理解会变得更加深刻。重新审视了本书涵盖的一些概念,强化了重要的主题,并就下一步的发展方向提供了一些建议。这本书是一本永远值得再次阅读的书,它能够一直为你提供指导。从某种...

  • 使基于node-webkit创建mac应用程序变得容易,从而简化了测试和构建过程。 它负责创建可执行文件,附加应用程序图标并相应地配置plist文件。 有关更多信息,请访问 ###非常alpha阶段目前,nuwk! 非常阿尔法。 该...

  • 最近测试了一些ai软件,市面上很多分列各种ai软件的推荐,但是对于ai软件写作的一些技巧,比较缺乏分析和总结。本文尝试从自身实践出发,分享个人的一些感受。具体写效果可以查看如下视频: ai智能写作软件...

  • 测试用例是测试工作中的一个重要的文档,而当前的ai技术大多数运用于文档编写,所以运用ai来编写测试用例,是首先能想到的实践方向。

  • 测试驱动开发,英文全称 test-driven development,简称 tdd,是一种不同于传统软件开发流程的开发方法。 在《程序员的职业素养》第五章,我第一次看到有关 tdd 内容,当时bob大叔向我展示了一种不可思议的编程开发...

  • 用例编号 测试项目 测试标题 重要级别 预置条件 输入数据 执行步骤 预期结果 1、问:你在测试中发现了一个bug,但是开发经理认为这不是一个bug,你应该怎样解决? 首先,将问题提交到缺陷管理库里面进行备案。 ...

  • afl(american fuzzy lop)是一种面向安全的模糊器,它采用新型的编译时检测和遗传算法来自动发现干净、有趣的测试用例,这些用例会触发目标二进制文件中的新内部状态。这大大改善了模糊代码的功能覆盖范围。该工具...

  • 编者按在过去的几年里,单元测试已成为我编写软件的核心环节,多亏了一种称为极限编程 (xp) 的简便编程方法(请参阅参考资源)。这种方法要求我为添加的每个函数编写单元测试,并且要维护这些测试。如果单元测试失败...

  • 应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个时间点之间的路径。使用 jdk 完成这项任务将非常痛苦和繁琐。现在来看看 joda time,一个面向 java™ 平台的易于使用的开源时间/日期库。正如...

  • 二哥,你能给我说说为什么 string 是 immutable 类(不可变对象)吗?我想研究它,想知道为什么它就不可变了,这种强烈的愿望就像想研究浩瀚的星空一样。但无奈自身功力有限,始终觉得雾里看花终隔一层。二哥你的...

  • 能畅快地书写代码是一大快事,想要解放自己,更多更快地写代码,就需要自动化来替代人工进行测试,谈到自动化,很容易想到的就是单元测试、接口测试、功能测试、性能测试、安全测试等等,其中部分环节常被忽略或是...

  • 关于银行项目的软件测试at some point during his or her career, a programmer might come across the following argument, presented by some colleague, partner, or decision maker:在他或她的职业生涯中的某个...

  • 这些机器人的敏捷性和衔接性非常惊人,可以如此准确和高效地完成这些有趣的动作。比起执行简单的任务,行动力有限,这是一个巨大的成就。 但在这个精心安排的程序中 在一个非常受控的环境中 它们完全是在做它们被...

  • 自2018年被评选为编程语言以来,python在各大排行榜上一直都是名列前茅。...为了避免出现“选择困难症”,我在此为大家准备了五种python类型的自动化测试框架,以供比较和讨论。 1.robot framewor

  • 2. 本文内容顺序:测试基础理论、测试岗经常被问到的场景题、linux知识点、智力题。 3. 本文阅读建议:我结合了自身的面试经历,把高频的、重要的知识点都用★标注了,★越多代表自己被问得次数越多。(当然这也只是...

  • “测试金字塔”是一个比喻,它告诉我们要把软件测试按照不同粒度来分组。它也告诉我们每个组应该有多少测试。虽然测试金字塔的概念已经存在了一段时间,但一些团队仍然很难正确将它投入实践。本文重新审视“测试...

  • 什么是mochamocha 是一个功能丰富的javascript测试框架,可以运行在nodejs和浏览器环境,使异步测试变得简单有趣。mocha 串联运行测试,允许灵活和精确地报告结果,同时映射未捕获的异常用来纠正测试用例。支

  • 2. 多读bug如果你和一个团队的软件测试工程师一起工作,那么请阅读 他们每天发的bug, 特别是那些针对你的测试部分的bug 。你可以从别人如何找到bug中学到很多东西。3. 多读代码找到你测试的那部分功能的代码。虽然...

  • a/b测试很有趣。市面上已经有许多方便易用的工具,我们都可以(也应该)做到。然而a/b测试不仅仅是进行测试那么简单。以下12个常见的a/b测试误区,浪费了许多企业的时间和金钱。 以下是我在工作中屡见不鲜的误区...

global site tag (gtag.js) - google analytics
网站地图