当有人第一次听到测试这个词的时候,感觉很可怕。虽然,测试帮助你对你写的代码有信心,而且从长远来看,它们是一种好处。假设你在一个团队中,正在开发一个应用程序。有人错误地交换了一些数据,而这在人工测试中被遗漏了。一个用户抱怨应用程序中的模拟数据。而另一个用户把这个反馈发给你,这样的消息轰炸了你的收件箱。
想象一下,写一个简单的单元测试来检查这样的手动错误,并在发布前提醒你。很简单,对吗?
在这篇关于测试和单元测试的介绍性文章中,我们将介绍以下内容:
为什么要测试?
作为一个软件开发者,你想确保你所写的代码是按照它应该有的方式工作。尽管这听起来很微不足道,但代码会断裂或发生回归。回归意味着你之前写的代码已经工作了,不再工作了。这就导致了更多的努力去修复它,而不是去做别的事情。为了尽量减少错误,减少人工测试的努力,并让你对代码有信心,你应该写测试。这将有助于你的工作。
· 减少bug
错误可能会通过人工测试并进入生产。即使是一个小小的错别字也会产生不利影响。如果有人错误地更新了不应该被触及的代码,写测试可以帮助你在开发的早期发现它们。
· 重构
你的目标是将某一段代码隔离开来单独测试。在这个过程中,你可能会重构你的代码,使其更加模块化,有精确的目标。
· 思考边缘案例
当你写测试的时候,你要思考可能发生的各种情况,然后写出关于它们的测试。
· 回归
在为应用程序添加一个全新的功能后,你不想破坏现有的代码或功能。这就是写测试的巨大帮助。如果你确信新功能不会破坏现有的功能并导致回归,你就会加快开发过程并节省时间。
要测试什么?
现在你知道为你的应用程序写测试是很有必要的,以便从长期来看受益于它。但是,到底要测试什么?这是每个人都想知道的问题。写什么测试?
如果你刚刚开始,最初的几个测试可以是关于测试应用程序的核心业务逻辑。例如,为你在应用程序中定期手动测试的东西编写测试,以确保它在生产中不会损坏。
什么是单元测试?
顾名思义,我们测试的是一个特定的单元--可以隔离出来单独测试的一大块代码。从网络调用,测试缓存的逻辑到你模型中的计算变量。我们有一个预定义的输入,然后在一个测试方法中检查特定的代码块的预期值和结果。
苹果公司为我们提供了一个名为XCTest的本地框架用于单元测试。还有其他的开源框架,如Quick和Nimble,但在这篇文章中我们将重点介绍XCTest。
了解XCTest和XCTestCase
XCTest是苹果公司的一个框架,帮助我们创建和运行单元、性能和UI测试。在这篇介绍性文章中,我们将只专注于创建单元测试。该框架为我们提供了两个主要的类--
·XCTest - 作为创建、管理和执行测试的基类。
·XCTestCase - 是定义测试案例、测试方法和性能测试的主要类,继承自XCTest 。
每个测试类都有一个生命周期,我们可以在运行前设置初始状态,在测试完成后进行清理。
XCTest 和XCTestCase 提供各种类型和实例方法,其中setUp() 和tearDown() 是两个主要的。
setUp() 方法是用来在测试运行前定制初始状态的。例如,在测试方法中初始化一个数据结构,并为每个测试案例重置其初始状态。
tearDown() 方法是用来在测试运行后进行清理。例如,为了删除任何引用,我们将初始化数据结构的实例设置为nil。
有两种类型的方法提供给我们,即
· 类方法,用于设置初始状态和对所有测试方法进行最终清理。在这里,我们分别覆盖了setUp() 和tearDown() 类方法。
· 实例方法,用于设置初始状态和对每个测试方法进行清理。同样地,我们分别覆盖setUp() 和tearDown() 实例方法。
现在我们已经完成了基本原理,是时候在Xcode中实际实现它了!所以,现在是时候继续学习更详细的Xcode单元测试教程了。
在Xcode中添加一个单元测试
每当你创建一个新项目时,你可以选择勾选包含测试。这些包括单元测试和UI测试。
如果你已经有了一个项目,你也可以添加一个单元测试包到其中。转到文件>新建>目标。
选择iOS Unit Testing Bundle,然后点击Next。
当你为你的项目创建一个新的单元测试目标时,它包括一个模板。它是测试案例的生命周期。让我们来看看文件的内容 :
setUp() 实例方法每次在测试方法运行前都会运行。你覆盖它来添加你自己的实现。如果你想在一个测试类中运行一次初始代码,请覆盖setUp() 类方法来代替。
与之前的setUp() 方法类似,要在每次测试方法后清理状态,请覆盖tearDown() 实例方法。对于在测试类中清理一次,请覆盖tearDown() 类方法来代替。
测试实例
在这篇文章中,我们将测试TallestTowers,这是一个显示世界各地最高的塔的应用程序,以及关于它们的信息。你可以在这里下载该项目。
在TallestTowers中,我们显示一个最高的塔的列表。而最重要的测试代码是检查该列表是否为空。
每当写一个测试时,我们在方法前加上 "test "这个词,这样Xcode就会理解它是一个可测试的函数。我们创建一个继承自XCTestCase 的类TowerStaticTests ,并在TowerStaticTests 中添加以下方法------:
class TowerStaticTests: XCTestCase {
func testTallestTowersShouldNotBeEmpty() {
XCTAssert(Tower.tallestTowers.count > 0)
}
}
我们从Tower 模型中获得静态变量tallestTowers ,并断言计数是否大于零。如果是的话,测试就会以绿色检查通过。
另一组要写的测试是针对Tower 的数据模型。我们计算位置,并将城市和国家名称与正确格式化的高度连接起来。我们将写一些单元测试,以确保这些计算的每个属性总是返回预期的输出。
首先,我们创建另一个继承自XCTestCase 的类,名为TowerInstanceTests 。我们声明一个类型为Tower的主题变量。在setUp() 方法中,用模拟数据初始化该变量。最后,我们重写tearDown() 方法,将主体设置为nil:
class TowerInstanceTests: XCTestCase {
var subject: Tower!
override func setUp() {
subject = Tower(name: "Empire State Building", city: "New York City", country: "USA", height: 381, yearBuilt: 1931, latitude: 40.748457, longitude: -73.985525)
}
override func tearDown() {
subject = nil
}
}
通过测试的长名字,我们特别提到了测试案例的内容。例如,测试地点应该由纬度和经度属性创建。所以,我们用骆驼的方式命名测试用例:
func testLocationShouldBeCreatedFromLatitudeAndLongitudeProperties() {
XCTAssertEqual(subject.location.latitude, 40.748457, accuracy: 0.00001)
XCTAssertEqual(subject.location.longitude, -73.985525, accuracy: 0.00001)
}
func testCityAndCountryShouldConcatenateCityAndCountry() {
XCTAssertEqual(subject.cityAndCountry, "New York City, USA")
}
func testFormattedHeightIncludesUnits() {
XCTAssertEqual(subject.formattedHeight, "381m")
}
通过写一些测试,我们感受到了如何写一个测试,以及测试什么!
命名的提示
每当写一个测试时,我们在方法前加上 "test "这个词,这样Xcode就会理解它是一个可测试的函数。
写长的方法名称。要具体。如果一个测试在众多的测试中失败了,那么名字的一瞥应该足以让你知道什么失败了。例如,如果testTallestTowersShouldNotBeEmpty() ,我们知道列表是空的。
调试一个单元测试
我们使用Xcode提供的标准工具调试,也是为了调试单元测试。在检查了任何逻辑或假设错误后,如果测试仍然失败或没有输出预期结果,我们可以使用测试失败断点。
转到断点导航器,选择添加按钮(+):
从下拉菜单中,选择添加测试失败断点。这在开始测试运行前设置一个特定的断点:
每当你运行一个测试,并且测试用例发布一个失败的断言时,该断点就会被触发。这有助于了解测试失败的地方,测试的执行也会停止:
启用代码覆盖
Xcode有内置的代码覆盖,以测试你的测试是否覆盖了所有的代码。要启用这个选项,请进入TallestTowers并点击Edit Scheme....从侧边栏选择测试选项,从分段控制中选择选项。勾选 "收集所有目标的覆盖率":
再次运行测试(Command + U)。从项目导航器中选择报告导航器,并点击最近的一个测试。选择覆盖率选项。你会发现所有被测试的文件,其覆盖率的百分比和可执行行:
由于我们彻底测试了Tower模型,我们可以看到95.5%的代码覆盖率。
要在编辑器中看到代码覆盖率,请选择编辑器选项并勾选代码覆盖率:
尽管拥有良好的代码覆盖率是很好的,但以100%为目标并不理想。
在CI中用Fastlane和Semaphore实现单元测试自动化
为了使测试过程自动化,我们将使用Fastlane,目的是简化部署。有多种方法可以安装Fastlane,我们将使用Homebrew。打开终端,运行命令:
brew install fastlane
改变目录到项目并运行:
fastlane init
现在,打开位于项目文件夹中的Fastfile ,并在其中添加以下几行 :
lane :tests do
run_tests(scheme: "TallestTowers")
end
最后,为了运行测试,在终端执行以下命令:
fastlane tests
为了使持续集成的过程自动化,你可以使用Semaphore服务。
结论
当临近截止日期时,很难专注于编写测试,但慢慢地你会发现,从长远来看,这是有好处的。编写测试可以提高代码质量,减少错误和回归,随着时间的推移加快开发进程。带着信心去写一些测试吧!
另外,花些时间为你的应用程序配置CI/CD,以专注于编写代码和提供良好的用户体验,而不是每次都手动交付应用程序。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理