在Swift中进行单元测试的详细指南

发表于:2022-7-21 09:41

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:方石剑    来源:稀土掘金

  当有人第一次听到测试这个词的时候,感觉很可怕。虽然,测试帮助你对你写的代码有信心,而且从长远来看,它们是一种好处。假设你在一个团队中,正在开发一个应用程序。有人错误地交换了一些数据,而这在人工测试中被遗漏了。一个用户抱怨应用程序中的模拟数据。而另一个用户把这个反馈发给你,这样的消息轰炸了你的收件箱。
  想象一下,写一个简单的单元测试来检查这样的手动错误,并在发布前提醒你。很简单,对吗?
  在这篇关于测试和单元测试的介绍性文章中,我们将介绍以下内容:
  为什么要测试?
  作为一个软件开发者,你想确保你所写的代码是按照它应该有的方式工作。尽管这听起来很微不足道,但代码会断裂或发生回归。回归意味着你之前写的代码已经工作了,不再工作了。这就导致了更多的努力去修复它,而不是去做别的事情。为了尽量减少错误,减少人工测试的努力,并让你对代码有信心,你应该写测试。这将有助于你的工作。
  · 减少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),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号