Skip to content

Latest commit

 

History

History
233 lines (118 loc) · 14.3 KB

Chapter_11_Testing.md

File metadata and controls

233 lines (118 loc) · 14.3 KB

Chapter 11 | 测试

当你一边说“我又不是测试人员”,一边准备跳过本章时,请先停下来想一想。事实上,测试对于软件工程师来说是一项重要的工作,因此 ,在面试中可能会出现测试问题。当然,如果你正在申请测试的工作(或者测试开发工程师),那么你就更有理由注意这方面的问题。

测试问题通常可以分为四类:(1)测试真实世界的对象(比如一支笔);(2)测试一个软件; (3)编写一个功能的测试代码; (4)对存在的问题进行故障排除。下面我们将介绍这四类情况的解决方法。

请记住,这四种情况下你最好不要假设输入或者用户会正常表现。对滥用的情况有心里准备,并为此做好计划。

面试官看重的是什么

从表面上看,测试问题似乎只是在提出大量测试用例。在某种程度上,这是对的。你确实需要拿出一个合理的测试用例列表。

但除此之外,面试官还想测试以下几个方面:

  • 全局理解:你是否了解软件的真正含义? 你能否正确确定测试用例的优先级? 例如,假设你被要求测试一个像 Amazon 这样的电商系统。确保在正确的位置显示产品图片非常重要,但更重要的是,付款应能可靠地工作,将产品添加到发货队列中,并且客户永远不会被重复收费。

  • 了解各个部分如何组合在一起:你是否了解软件是如何工作的,以及如何将其融入更大的生态系统中? 假设要求你测试 Google Spreadsheets,对打开、保存和编辑文档进行测试很重要。但是,Google Spreadsheets 是更大的软件生态的一部分。你需要测试它与 Gmail、插件以及其他组件的集成。

  • 组织:你是用一种结构化的方式来处理问题,还是对你想到的任何东西只是脱口而出?有些求职者,当被要求给出一个相机的测试用例时,只会想到什么就说什么。一个好的候选人会将这些部分细分成几类,比如拍照、图像管理、设置等等。这种结构化方法还将帮助你更全面地创建测试用例。

  • 实用性:你真的能创建合理的测试计划吗?例如,如果用户报告说,当他们打开特定的图像时,软件崩溃了,而如果你只是告诉他们要重新安装软件,这通常是不太实际的。你的测试计划对于公司而言必须切实可行的。

展示这些方面将证明你有能力成为测试团队中有价值的一员。

测试真实世界的对象

有些应聘者被问到诸如如何测试一支笔之类的问题时会感到惊讶。毕竟,你应该测试软件,对吗?也许吧,但这些“现实世界”的问题仍然很常见。让我们通过一个例子来完成这个过程。

问题:你如何测试一个回形针?

Step 1:谁会使用它? 又为什么呢?

你需要与面试官讨论谁正在使用该产品,以及出于什么目的。答案可能不是你想的那样。答案可能是“老师,为了把文件放在一起”,也可以是“艺术家,为了弯曲成动物的形状”,或者是两者兼有。这个问题的答案将决定你如何处理剩下的问题。

Step 2:有哪些用例?

对你来说,列出用例列表是很有用的。在当前情况下,用例可能只是简单地以不损坏纸张的方式将纸张固定在一起。

对于其他问题,可能有多个用例。例如,产品可能需要能够发送和接收内容,或者编写和删除内容,等等。

Step 3:使用范围是什么?

使用范围可能意味着在一次使用中最多可持有 30 张纸而不会造成永久性损坏(例如,弯曲),以及 30 至 50 张纸只允许产生最小程度的永久性弯曲。

这个范围也可以扩展到到环境方面的因素。例如,回形针在非常温暖的温度(90~110 华氏度)下能够工作吗?那极度寒冷的情况下呢?

Step 4:压力/故障情况有哪些?

没有产品是防故障(fail-proof)的,所以分析故障条件需要成为测试的一部分。和你的面试官好好谈谈什么时候产品故障是可以接受的(甚至是必要的),以及故障的含义是什么。

例如,如果要测试一台洗衣机,则可能会决定该洗衣机至少应能处理 30 件衬衫或裤子。装载 30~45 件衣服可能会导致轻微故障,例如衣服没有被充分清洗。如果衣服超过 45 件,极端的故障可能是可以接受的。但是,这种情况下的极端故障可能只是意味着机器永远不会打开水工作,这当然不应该意味着洪水或火灾。

Step 5:你将如何执行测试?

在某些情况下,讨论执行测试的细节可能也很重要。例如,如果你需要确保一把椅子可以正常使用五年,你可能不能把它放在家里等五年。相反,你需要定义什么是“正常”使用(每年有多少人“坐在”这个座位上?扶手呢?)然后,除了进行一些手动测试之外,你可能还需要一台机器来自动化执行某些使用。

测试一个软件

测试软件实际上与测试现实世界对象非常相似。主要区别在于,软件测试通常更加注重执行测试的细节。

请注意,软件测试有两个核心方面:

  • 手动测试与自动化测试:在理想的情况下,我们可能希望使所有内容实现自动化,但这几乎是不可行的。有些东西用手工测试会更好,因为有些特性太定性,计算机无法有效地检查(比如内容是否是代表色情)。此外,尽管计算机通常只能识别被告知要查找的问题,但人类的观察可能会发现尚未专门检查的新问题。人和计算机都是测试过程的重要组成部分。

  • 黑盒测试与白盒测试:这一区别指的是我们对软件的访问程度。在黑盒测试中,我们只是按原样获得该软件,并需要对其进行测试。通过白盒测试,我们可以通过其他编程方式来测试各个功能。我们还可以自动化一些黑盒测试,尽管这肯定要困难得多。

让我们从头到尾逐步介绍一种解决方法。

Step 1:我们要进行黑盒测试还是白盒测试?

虽然这个问题通常可以推迟到后面的步骤,但我喜欢尽早解决它。询问面试官你是在做黑盒测试还是白盒测试,或者两者都做。

Step 2:谁会使用它? 又为什么呢?

软件通常具有一个或多个目标用户,并且在设计功能时考虑了这一点。例如,如果要求你在 Web 浏览器上测试用于家长控制的软件,则目标用户既包括父母(正在实施件屏蔽),也包括孩子(作为件屏蔽的接收者)。你可能还会有“客人”(既不应实施也不应该接受件屏蔽的人)。

Step 3:有哪些用例?

在软件屏蔽场景中,针对父母的用例包括安装软件、更新控件、删除控件,当然还有他们自己的网络使用情况。对于儿童,用例包括访问合法内容和“非法”内容。

请记住,并不是由你来神奇地决定用例。这应该是你和面试官对话的结果。

Step 4:使用范围是什么?

现在我们已经定义了模糊的用例,我们需要弄清楚这到底意味着什么。一个网站被屏蔽意味着什么?应该只屏蔽“非法”页面还是整个网站?应用程序应该“学习”什么是不良内容,还是基于白名单或黑名单?如果它想知道什么是不合适的内容,什么程度的误报(false positives)或漏报(false negatives)是可以接受的?

Step 5:压力/故障情况有哪些?

当软件发生故障时(发生故障是不可避免的),故障应该是什么样的呢?显然,软件故障不应该使计算机崩溃。相反,软件应该只允许一个被屏蔽的站点,或者禁止一个被允许的站点。在后一种情况下,你可能需要讨论使用父母密码进行选择性覆盖的可能性。

Step 6:有哪些测试用例? 您将如何执行测试?

这就是手动测试和自动测试、黑盒测试和白盒测试之间的区别真正发挥作用的地方。

step 3 和 4 应该大致定义了用例。在 step 6 中,我们进一步定义它们,并讨论如何执行测试。你测试的具体情况是什么?这些步骤中哪些可以自动化?哪些需要人工干预?

请记住,虽然自动化可以让你进行一些非常强大的测试,但它也有一些明显的缺点。手动测试通常应该是测试程序的一部分。

当你浏览这张清单时,不要一口气说出你能想到的每一个场景。它是杂乱无章的,你肯定会错过主要类别。相反,应该以结构化的方式处理这个问题。将你的测试分解为主要组件,然后从那里开始。这样你不仅可以给出一个更完整的测试用例列表,而且还会显示出你是一个结构化、有条理的人。

测试一个功能

在许多方面,测试一个功能是最简单的测试类型。由于测试通常仅限于验证输入和输出,所以对话通常更简短,也不那么模糊。

但是,请不要忽略与面试官交谈的价值。您应该与面试官讨论任何假设,特别是关于如何处理特定情况。

假设你被要求编写代码来测试 sort(int[] array),该方法对整数数组进行排序。你可以按照以下步骤进行。

Step 1: 定义测试用例

通常,你应该考虑以下类型的测试用例:

  • 正常情况:它为典型输入生成正确的输出吗?记住,这里要考虑潜在的问题。例如,由于排序通常需要某种类型的划分,因此可以合理地认为该算法可能会在元素数量为奇数的数组上失败,因为它们无法均匀划分。你的测试用例应列出这两个例子。

  • 极端情况:传入空数组时会发生什么?或者一个非常小的(一个元素)数组?如果你传入一个很大的呢?

  • Null 和“非法”输入:当给定非法输入时,代码应该如何表现是值得考虑的。例如,如果你测试一个可以生成第n个斐波那契数的功能,那么你的测试用例应该包括 n 为负的情况。

  • 奇怪的输入:有时会出现第四种输入:奇怪的输入。当传入一个已经排序的数组时会发生什么?或者是一个倒序排列的数组?

生成这些测试确实需要你了解正在编写的功能。如果你不清楚这些限制,你需要先问面试官。

Step 2:定义预期的结果

通常,预期的结果是显而易见的:正确的输出。然而,在某些情况下,你可能希望验证其他方面。例如,如果 sort 方法返回数组的新排序副本,则你可能应该验证原始数组是否没有被修改。

Step 3:编写测试代码

一旦定义了测试用例和结果,编写用于实现测试用例的代码就应该非常简单。你的代码可能是这样的:

1 	void testAddThreeSorted() {
2 		Mylist list = new Mylist();
3 		list.addThreeSorted(3, 1, 2); // Adds 3 items in sorted order
4 		assertEquals(list.getElement(0), 1);
5 		assertEquals(list.getElement(1), 2);
6 		assertEquals(list.getElement(2), 3);
7 	}

故障排除问题

最后一种问题是说明如何调试或解决现有问题。许多候选人都对这样的问题不屑一顾,给出了不切实际的答案,例如“重新安装软件”。实际上你可以像对待其他事情一样,用一种结构化的方式来处理这些问题。

让我们通过一个例子来解决这个问题:当你在 Google Chrome 团队中工作时,收到了一个bug报告:Chrome 在启动时崩溃。你会怎么做?

重新安装浏览器可能会解决该用户的问题,但对其他可能遇到相同问题的用户没有帮助。你的目标应该是了解实际发生了什么,以便开发人员可以对其进行修复。

Step 1:了解情况

你应该做的第一件事是问问题,尽可能多地了解情况。

  • 用户遇到这个问题有多久了?

  • 这是什么版本的浏览器?什么操作系统?

  • 这个问题经常发生吗?或者多久发生一次?什么时候发生?

  • 是否有启动错误报告?

Step 2: 解决问题

既然你已经了解了该场景的详细信息,您就可以将问题分解为可测试的单元。在这种情况下,你可以想象情况的流程如下:

  1. 进入 Windows 开始菜单。

  2. 单击 Chrome 图标。

  3. 浏览器实例启动。

  4. 浏览器加载设置。

  5. 浏览器发出 HTTP 主页请求。

  6. 浏览器获得 HTTP 响应。

  7. 浏览器解析网页。

  8. 浏览器显示内容。

在此过程中的某个时刻,某些操作会失败,并导致浏览器崩溃。一个强大的测试人员将遍历可能导致此场景的各个因素以诊断问题。

Step 3:创建特定的,可管理的测试

以上每个组件都应具有切合实际的说明——你可以要求用户执行的操作或可以自己执行的操作(例如,在自己的计算机上复制步骤)。在现实世界中,你将与客户打交道,并且你不能给他们下达他们不能或不愿做的指示。。


Interview Questions


  • 11.1 错误(Mistake):找出下列代码中的错误:

    unsigned int i;
    for (i = 100; i >= 0; --i)
    printf("%d\n", i);

    提示:#257, #299, #362

  • 11.2 随机崩溃(Random Crashes):给定一个在运行时崩溃的应用源代码。在调试器中运行十次之后,你会发现它不会在同一个地方崩溃。应用程序是单线程的,并且只使用 C 标准库。哪些编程错误可能导致这个崩溃?你将如何测试每一个?

    提示:#325

  • 11.3 象棋测试(ChessTest):我们在国际象棋游戏中使用以下方法:boolean canMoveTo(int x, int y)。此方法是 Piece 类的一部分,并返回棋子是否可以移动到位置 (x, y)。解释如何测试这个方法。

    提示:#329, #401

  • 11.4 没有测试工具(No Test Tools):不使用任何测试工具,如何对网页进行负载测试?

    提示:#313, #345

  • 11.5 测试笔(Test a Pen):你将如何测试一支笔?

    提示:#140, #164, #220

  • 11.6 测试ATM(Test an ATM):你将如何测试分布式银行系统中的 ATM ?

    提示:#210, #225, #268, #349, #393

提示从第 662 页开始。