全栈测试?平衡单元测试和端到端测试
作者:网络转载 发布时间:[ 2016/7/26 14:08:40 ] 推荐标签:全栈测试 单元测试
处理交互
当每次点击都重新加载页面时,端到端测试更可靠,因为底层工具知道要等待一个页面重新加载。当用户交互只是改变DOM时,难度大了,因为工具不知道什么“事情”正在发生,也无法“等待事情完成”。
当测试需要同一个不会根据用户动作重新加载的页面交互时,需要一种方法能够在开始断言发生了什么之前等待DOM操作完成。如果不等待,那么如果测试开始断言时DOM还没有更新,测试会无谓地失败。
像在标记中使用标识定位要操作的DOM元素一样,我们也可以把它们用在这里。任何新增或变化的标记都应该有某种在交互失败或没有发生的情况下不会出现的标识。换句话说,你不必为了等待DOM事件而在测试中进行休眠调用——DOM中应该包含可供测试显式等待的标识。
例如,假设我们想要测试一个动作为用户生成了一条成功的消息。假设实现方法是发出一个AJAX请求,当调用结束时向DOM中插入一条消息。一个基本的实现可以像下面这样做:
function purchase(productId) {
$.post(
"/products/",
{ "id": productId }
).done(function() {
$(".header").html(
"<div class='alert-success'>Your order was placed</div>");
}).fail(function() {
$(".header").html(
"<div class='alert-failure'>There was a problem</div>");
});
你可以通过配置让测试等待一个使用了CSS类alert-success的元素出现,然后断言它的内容。这意味着,如果页面需要任何其他使用那个类的元素,那么测试会不可靠或被破坏。虽然你可以将其限制在HTML头里,但这只是缓兵之计。
作为替代,可以使用data-test-属性:
function purchase(productId) {
$.post(
"/products/",
{ "id": productId }
).done(function() {
$(".header").html(
"<div data-test-purchase-successful class='alert-success'>Your order was placed</div>");
}).fail(function() {
$(".header").html(
"<div data-test-purchase-failed class='alert-failure'>There was a problem</div>");
});
虽然这增加了标记的字节,但它让你可以编写一个能够不受某些视觉变化影响的可靠测试。只要页面流程是在一次成功的购买后显示一条消息,那么可视化实现可以修改而又不破坏测试。这是你想要的,这是一种权衡。你也可以牺牲掉这份自信,创建小起码的标记,但当显示效果变化时,你要么花时间修复测试,被迫手动QA,要么发布没有经过充分测试的软件。
如今的端到端测试工具,如Capybara,包含你需要的所有功能。它提供了方法,可以在继续测试过程之前等待DOM元素出现,断言页面特定部分的内容,同表单元素交互。大多数其他Web应用程序栈都提供了类似的工具。不管怎样,你可以将测试库与像PhantomJS这样的无界面浏览器结合,从而使端到端测试出奇地快速可靠。
还有一点值得注意,是在一个分布式的环境中如何完成这项工作。
当“应用”多于一个
当对单个整体系统进行测试时,上述技术完全够用了。然而,如果是对一个较为分散的系统进行测试,情况要复杂些了。假设你正致力于一个面向客户的应用程序,但它必须从另一个系统获取库存数据。你如何为此编写一个测试呢?
首先,记住你在测试什么。端到端测试是测试用户交互。这意味着,端到端测试不用负责断言远程服务的功能,也不用负责断言应用程序正确地消费了那个远程服务。
测试服务消费的佳方式是使用“消费者驱动的契约(consumer-driven contracts)”,这是一种单元测试的形式(至少在这篇博文中我所做的宽泛界定中是这样)。
对于在端到端测试中如何模拟远程服务,至此仍然没有定论。你可以搭建该服务的一个实际版本,但这并不是很好。你终不得不管理那个服务的内部数据存储以及它所依赖的服务。那会使复杂性迅速增加,难以管理。
一个常见的选择是使用一个HTTP层的模拟系统。在Ruby中,VCR是一款具备这种功能的工具。你录制同真实服务交互以建立HTTP协议往返的过程,在随后运行测试时,模拟系统会回放录制好的交互,而不必使用网络。如果单元测试覆盖了服务的正确消费,那么这对于端到端测试会很有效。
另一个选择是搭建一个经过简化的模拟服务,该服务返回预先准备好的数据。应用会像平常一样进行HTTP调用,但调用的是一个预先准备好、只向应用返回静态已知数据的服务。这需要提前做些配置,但对简单的服务交互很有效。如果应用程序需要在服务中存储状态,并有一个漫长的往返“对话”,那么这项技术要难一些了。
我的建议是首先尝试模拟HTTP,因为那既简单又快捷。
现在,我们知道在端到端测试中测试什么以及如何测试,那么单元测试呢?
单元测试
回想一下,对于什么应该进行端到端的测试,我们的标准是用户流程。其思想是,虽然整个系统有许多可能的逻辑流程,但能对用户体验产生影响的要少很多。单元测试是要测试那些逻辑流程的剩余部分。
这让我们可以快速可靠地断言系统大部分功能的正确行为。换句话说,虽然我们可以使用端到端测试断言整个系统中每个可能的流程,但那没有必要,而且会非常缓慢和脆弱。
例如,假设一个结算功能有两个用户流程:一个是购买成功,一个是购买失败,用户必须重试。那会有两个端到端测试。让我们进一步假设,后台有如下可能性:
客户的信用卡正确扣款;
与客户银行的通信存在问题,但我们想假装它是成功的,并在稍后扣款;
客户的信用卡被拒绝;
客户的信用卡过期。
这是四个流程,所以我们希望有四个单元测试可以断言其中每一种情况都得到了正确处理。是的,会有重复覆盖。在端到端测试中,我们可能会创建成功扣款和拒绝两个测试来处理该功能的两个用户流程,因此,当编写单元测试时,我们的覆盖率会超过理论上的需要。
再一次,这是一种权衡,但重要的是,单元测试可以很好地覆盖你的类。这允许它们改变位置、用途,而且更容易修改。
关于如何编写单元测试,有许多许多的理论,远远超出了我们这里的讨论范围。我的建议是采用一种对你有用同时也容易跟别人解释的技术,并一直使用。
对于单元测试,困难的部分是决定代码设计要在多大程度上为测试考虑。这类似我们如何为了测试向HTML中增加属性和其他标识——那些工件只是因为我们要测试而存在。在编写单元测试时,你会面临同样的选择。
例如,假设Purchaser类实现了信用卡扣款代码。假设它将使用第三方提供的AwesomePayments进行实际地扣款。
class Purchaser
def charge(purchase)
AwesomePayments.charge(purchase.customer.id,purchase.amount)
rescue => ex
try_again_later(purchase.id)
end
# ...
end
上述代码清晰易懂,在不需要单元测试的情况下,这可能是理想的设计了。然而,为了让测试更简单,我们可能想控制AwesomePayments的实例:
class Purchaser
def initialize(awesome_payments = AwesomePayments)
@awesome_payments = awesome_payments
end
def charge(purchase)
@awesome_payments.charge(purchase.customer.id,purchase.amount)
rescue => ex
try_again_later(purchase.id)
end
end
现在,可以在测试时传入AwesomePayments的模拟实现,从而更好地控制测试。测试已经影响了我们的设计(虽然这里的影响比较小)。你甚至可以说,这个类是更好的代码。但情况并非总是如此。
我会使用同你处理端到端测试一样的标准:做让生活更轻松的事,但不要做过头,务必要恰到好处。
小结
从头到尾实现一个特性的能力取决于从头到尾测试它的能力。由QA团队或客户测试代码的反馈循环存在极大的危害。即使有QA团队,他们也不应该找到Bug,如果要想快速发布软件,不会介意编写用户行为的端到端测试。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
iOS单元测试mocha、chai、sinon和istanbul实现百分之百的单元测试覆盖率关于单元测试的总结及思考编写更好的Java单元测试的7个技巧Android单元测试框架Robolectric3.0介绍(一)使用Kiwi单元测试总结单元测试如此重要,为什么你不知道Python单元测试??使用装饰器实现测试跳过和预期故障对Controller的单元测试写好单元测试的10个技巧单元测试的重要性Angular单元测试系列??Component、Directive、Pipe 以及ServiceAndroid单元测试的整理提升单元测试体验的利器--Mockito使用总结iOS UnitTest单元测试Vue的单元测试探索(二)
更新发布
常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11系统性能测试及调优前期准备
2021/4/15 14:41:29国内比较好用的5款测试管理工具
2021/3/25 17:23:31热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南