0


Xcode单元测试中文教程:入门到精通

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文旨在为iOS和macOS开发者介绍Xcode单元测试的中文版教程,重点讲解如何在Xcode集成开发环境中编写和执行单元测试,以提高软件质量。教程不仅提供了基础概念和XCTest框架的使用方法,还包括断言使用、模拟对象和测试覆盖率等高级主题。同时,也针对初学者提供了入门指南,以及如何查看代码覆盖率报告和进行UI测试。中文版教程有助于降低语言障碍,让开发者能够通过实践来提升代码的稳定性和可靠性。 xcode

1. Xcode单元测试中文版概览

在现代软件开发领域中,编写高质量的代码并不是唯一的挑战,如何确保这些代码在不断更新和维护过程中依旧可靠,已经成为了一个关键问题。为了解决这一问题,开发者们引入了一种称为单元测试的实践方法。单元测试是一种程序测试的类型,用于验证代码中最小的可测试部分是否按预期工作。在 iOS 和 macOS 开发中,单元测试尤为重要,它保证了软件功能的正确性和稳定性。

Apple 为开发者提供了强大的工具——Xcode,它集成了一个单元测试框架,名为

 XCTest 

。Xcode 单元测试不仅可以帮助开发者在开发过程中快速发现并修复缺陷,还能够提高代码质量和团队协作效率。本章将为读者介绍 Xcode 单元测试的基础知识,以及如何使用它来改善你的开发流程。接下来的章节中,我们将深入探讨如何在 Xcode 中创建和运行单元测试,以及测试驱动开发(TDD)和行为驱动开发(BDD)等高级主题。

通过学习本章内容,你将掌握以下关键点: - 单元测试在软件开发流程中的重要性。 -

 XCTest 

框架的基本使用方法。 - 如何将单元测试融入到你的日常开发实践中。

2. 单元测试在iOS/macOS开发中的作用

单元测试是软件开发中用来验证单个代码单元(如一个函数、方法或类)正确性的过程。在本章中,我们将探索单元测试在iOS和macOS开发中的作用,具体讨论它的基本概念、重要性以及它带来的优势。

单元测试的基本概念

单元测试的定义

单元测试是软件测试中最小的测试单位。它面向软件中的一个模块或功能点,通常由软件开发人员编写和执行。其主要目的是在软件开发过程中尽早发现并修复缺陷,以保证每个部分都按照预期工作。

单元测试的目的和重要性

单元测试的目标是验证软件的最小可测试部分的功能和行为是否符合设计要求。它不仅减少了缺陷流入下游流程的风险,而且在代码重构时也提供了额外的安全网。此外,单元测试的编写过程可以促进开发人员更深入地思考代码的结构和设计,从而提升代码质量。

单元测试的重要性主要体现在以下几个方面:

  • ** 质量保证 ** :单元测试是保证软件质量的基石,它能够在开发早期发现缺陷,避免它们扩散到整个软件产品中。
  • ** 设计改进 ** :编写单元测试会迫使开发人员考虑如何更好地设计代码,从而提高模块的独立性和可测试性。
  • ** 开发效率 ** :单元测试有助于开发人员快速定位问题所在,缩短调试时间,提高开发效率。

单元测试在iOS/macOS开发中的优势

保证代码质量

在iOS和macOS应用开发中,单元测试特别重要,因为它们能够确保应用程序的各个组件按预期工作。由于Apple的操作系统和应用商店对质量和性能有着较高的要求,因此单元测试在开发过程中扮演了关键角色。

  • ** 持续集成 ** :单元测试可以与持续集成系统相结合,确保每次代码提交时应用程序的稳定性,从而保护应用的质量不会因为新的提交而降低。
  • ** 行为一致性 ** :应用程序功能复杂度增加时,单元测试可以帮助保持功能的一致性,减少因新功能引入而导致的现有功能退化。

降低维护成本

良好的单元测试可以帮助开发团队在进行代码维护时节约时间。当系统的一部分代码发生变化时,通过单元测试可以迅速识别出哪些部分受到了影响,哪些部分仍然稳定。

  • ** 快速反馈 ** :自动化测试可以提供快速反馈,帮助开发人员了解改动是否引入了新的问题。
  • ** 风险控制 ** :通过单元测试覆盖的代码更改风险较低,因为测试可以验证新的改动没有破坏现有功能。

提高开发效率

单元测试不仅仅是质量保障的工具,它也是提升开发效率的工具。测试驱动开发(TDD)是一种常见的开发实践,强调在编写功能代码之前先编写测试用例,这迫使开发人员提前考虑代码的设计和实现,从而提升代码质量,并最终提高开发效率。

  • ** 加快开发节奏 ** :在TDD实践中,开发人员在编写完通过的测试之后编写实际的代码实现,从而加快了开发的节奏。
  • ** 减少问题定位时间 ** :良好的单元测试覆盖意味着在出现问题时,开发人员可以快速定位到问题的源头,避免了在庞大代码库中大海捞针的情况。

通过本章内容的介绍,我们可以看到单元测试在iOS和macOS开发中的核心作用,以及它所带来的质量和效率提升。单元测试是构建高质量软件不可或缺的一环,无论对于个人开发者还是大型开发团队,它都是提高生产力和软件质量的关键实践。

在下一章节,我们将进一步深入了解Xcode提供的单元测试框架,如SenTestingKit和XCTest,它们如何帮助开发者更有效地进行单元测试工作。

3. 苹果测试框架SenTestingKit和XCTest的介绍

3.1 SenTestingKit框架概述

3.1.1 SenTestingKit的历史和特点

SenTestingKit是苹果公司在Xcode 3.0版本中引入的一个单元测试框架,它在早期的iOS和macOS应用开发中扮演了关键角色。SenTestingKit的设计初衷是提供一个简单的接口,以便开发者能够快速地为他们的应用编写单元测试。它的历史可以追溯到早期的Objective-C编程环境,那时对测试框架的需求日益增长,为了适应这种需求,苹果公司创建了SenTestingKit。

SenTestingKit的特点主要表现在以下几个方面:

  • ** 轻量级 ** : 与后来的XCTest相比,SenTestingKit更加轻量级,它并没有太多的依赖,使得开发者可以较容易地集成到各种项目中。
  • ** 易用性 ** : 提供了一组简单的API,使得编写测试用例变得直观且容易上手。
  • ** 集成度 ** : 与Xcode紧密集成,可以轻松地在Xcode中运行测试并查看测试结果。

3.1.2 SenTestingKit的使用场景

尽管SenTestingKit已经被XCTest所取代,但在某些遗留项目和特定场景下,它仍有其应用价值。SenTestingKit适用于:

  • ** 老旧项目维护 ** : 对于那些仍在使用Xcode早期版本的遗留项目,SenTestingKit可能是编写测试的唯一选择。
  • ** 轻量级测试 ** : 对于不需要复杂测试场景的简单应用,SenTestingKit提供了一个快速测试的解决方案。
  • ** 兼容性测试 ** : 开发者可能需要为特定版本的iOS或macOS系统提供测试支持,SenTestingKit可以用来进行这些旧版本系统的兼容性测试。

3.2 XCtest框架的创新与发展

3.2.1 XCtest的主要特性

XCTest是苹果公司在Xcode 5中推出的新一代测试框架,它继承了SenTestingKit的优点,并且引入了许多创新特性。XCTest的目标是进一步简化测试过程,并使测试更加易于理解和维护。以下是XCTest的一些主要特性:

  • ** 集成度更高 ** : 完全集成到Xcode中,支持测试结果的快速查看和测试代码的即时调试。
  • ** Xcode UI测试 ** : 通过XCUITestCase为开发者提供了一种新的UI测试能力,能够模拟用户的交互行为。
  • ** 性能测试 ** : 支持性能测试,方便开发者分析代码的执行性能。
  • ** 异步测试支持 ** : 提供了对异步测试的支持,允许测试在后台线程中执行。

3.2.2 XCtest与SenTestingKit的比较

XCTest和SenTestingKit之间存在显著差异,主要体现在以下几个方面:

  • ** 测试方法 ** : SenTestingKit使用 Assert 方法进行测试,而XCTest引入了更易于理解的 XCTAssert 方法。
  • ** 测试组织 ** : 在SenTestingKit中,测试用例被组织在 SenTestCase 类中,而XCTest使用了基于协议的方式组织测试,如 XCTestCase 协议。
  • ** 异步测试 ** : SenTestingKit不支持异步测试,而XCTest支持,这对于UI测试尤为重要。
  • ** 性能 ** : XCtest在性能上更优化,特别是处理大量测试用例时。

3.2.3 XCtest的测试环境配置

配置XCTest的环境相对简单,以下是一个基本的步骤示例:

  1. 打开Xcode项目。
  2. 在项目导航器中右键点击,选择“New File…”。
  3. 在“iOS”或“macOS”下找到“Unit Test Case Class”模板,然后创建一个新的测试类。
  4. 在新创建的测试类中,编写测试用例。
  5. 在项目设置中,选择“Build Settings”,找到“Testable”选项,并勾选你的应用程序目标,以便它可以在测试中被加载。
  6. 点击Xcode工具栏中的“Product”菜单,然后选择“Test”以运行测试。

XCTest框架的配置只需要简单的几步,便可以开始编写并执行测试用例。这一过程得益于Xcode的紧密集成和易用性设计,确保了测试过程的流畅和高效。

4. 如何在Xcode中创建和运行单元测试

单元测试是确保代码质量的基石之一。在本章节中,我们将深入了解如何在Xcode环境中创建、配置、运行以及调试单元测试。这一过程不仅需要对开发工具的熟悉,还需要掌握测试的基础知识和最佳实践。

4.1 创建单元测试的基本步骤

4.1.1 新建测试目标

在Xcode中创建一个新的单元测试目标,是开始编写单元测试的第一步。通过这一目标,Xcode能够理解你需要测试的代码和预期的结果。

新建测试目标的步骤
  1. 在Xcode的项目导航器中,右键点击项目名称,选择"New Target..."。
  2. 在弹出的对话框中,选择"Test"类别下的"XCTest",点击"Next"。
  3. 输入测试目标的名称,比如"MyProjectTests",并选择相应的平台,然后点击"Finish"。

这将生成一个新的测试工作区和测试类,你可以在这里编写和运行你的测试用例。

4.1.2 编写测试用例

测试用例是单元测试的核心,它描述了你希望代码执行的预期行为。在XCTest中,我们使用

 XCTestCase 

子类来编写测试用例。

编写测试用例的步骤
  1. 在Xcode中,打开刚才创建的测试目标文件,它通常以 MyProjectTests.swift 命名。
  2. 创建一个继承自 XCTestCase 的类,用于存放所有的测试用例。
  3. 编写测试方法,测试方法以 test 为前缀,比如 func testExample()
  4. 在测试方法中,使用 XCTAssert 等断言函数来检查代码执行结果是否符合预期。
  5. 保存并运行测试。
import XCTest

class MyTests: XCTestCase {

    func testExample() {
        let result = 2 + 2
        XCTAssertEqual(result, 4, "2 + 2 should equal 4")
    }

    // 更多的测试方法...
}

4.2 运行和调试单元测试

4.2.1 使用Xcode运行测试

一旦编写好了测试用例,我们可以使用Xcode的图形界面来运行它们,或者使用命令行工具。

使用Xcode运行测试的步骤
  1. 在项目导航器中选择测试目标。
  2. 转到Xcode的"Product"菜单,选择"Test",或者使用快捷键 ⌘ + U
  3. 观察Xcode的测试运行器界面,它会显示所有测试用例的运行状态。
  4. 如果测试失败,Xcode会提供失败用例的详细信息,包括失败的原因和位置。

4.2.2 分析测试结果

分析测试结果是理解代码行为和缺陷的关键。通过Xcode的测试结果,我们可以快速定位到问题所在。

分析测试结果的步骤
  1. 查看测试结果面板,失败的测试会有红色的图标,成功的测试会有绿色的图标。
  2. 点击失败的测试用例,Xcode会在源代码编辑器中高亮显示失败断言的位置。
  3. 根据失败信息,调整代码或测试用例,直到测试通过。
  4. 当所有测试用例通过后,你的代码质量会有很大的提升。
  5. 定期运行测试来确保新的代码更改没有破坏现有的功能。
// 以Xcode 11以上版本为例,查看测试运行器快捷操作

// 运行所有测试
⌘ + U

// 重新运行上一次失败的测试
⌘ + ⇧ + U

// 快速打开测试导航器
⌘ + ⌥ + T

// 打开测试结果概览
⌘ + 6

在本章节中,我们学习了如何在Xcode中创建和运行单元测试,包括创建测试目标、编写测试用例、运行测试以及分析测试结果。这些基础知识为后续章节中关于测试驱动开发、行为驱动开发以及测试覆盖率的学习打下了坚实的基础。在下一章节中,我们将深入探讨测试驱动开发(TDD)和行为驱动开发(BDD)的流程与实践。

5. 测试驱动开发(TDD)和行为驱动开发(BDD)

5.1 测试驱动开发(TDD)的流程与实践

5.1.1 TDD的定义和原则

测试驱动开发(Test-Driven Development, TDD)是一种软件开发方法论,核心思想是在编写功能代码之前先编写测试用例。它提倡通过短迭代周期开发高质量的软件,而测试用例的编写在每个迭代开始前完成。

TDD流程通常遵循以下三个基本步骤的循环(也被称作“红-绿-重构”循环): 1. ** 编写测试用例(红色) ** - 开发人员首先编写一个失败的测试用例。 2. ** 编写功能代码(绿色) ** - 然后编写最小的功能代码使测试通过。 3. ** 重构代码 ** - 最后重构功能代码并确保所有测试仍然通过。

TDD的原则包括: - 先编写测试用例。 - 每个功能模块必须有对应的测试用例。 - 只有测试未通过时才编写功能代码。 - 通过重构来提升代码质量。

5.1.2 TDD的实际应用案例

实际应用中,TDD可以帮助开发者专注于当前功能的实现,并确保每一小步的进展都符合预期。例如,在使用Xcode进行iOS应用开发时,可以按照以下步骤实践TDD:

  1. 创建一个新的 XCTestCase 类。
  2. 在该测试类中编写一个断言,比如检查某个函数是否返回正确的值。
  3. 运行测试,看到失败的状态。
  4. 编写最简单的代码让测试通过。
  5. 通过重构来优化代码。
  6. 重复以上步骤,添加更多的测试用例和功能代码。

一个简单的示例代码如下:

class MathTests: XCTestCase {
    func testAddition() {
        let result = 2 + 3
        XCTAssertEqual(result, 5, "预期结果为5")
    }
}

在这个例子中,

 XCTAssertEqual 

是一个断言方法,用来检查

 result 

是否等于5。当运行测试时,它会显示为红色(失败),随后编写功能代码使得测试通过变为绿色,最后进行重构确保代码质量和测试覆盖。

5.2 行为驱动开发(BDD)的流程与实践

5.2.1 BDD的定义和优势

行为驱动开发(Behavior-Driven Development, BDD)是一种更接近业务需求的开发模式,它强调软件的行为,将业务、开发者和测试人员聚合在一起,共同理解软件应该做什么。

BDD的原则主要有: - 以用户故事和验收标准作为需求的基础。 - 涉及利益相关者的实际行为和期望。 - 使用领域特定语言(DSLs)来描述软件的行为。

BDD的优势包括: - 易于理解和协作,因为它是从业务角度出发。 - 明确了软件行为的预期,减少了需求误解。 - 有助于在软件开发早期阶段发现需求问题。

5.2.2 BDD的实际应用案例

BDD实践中常用的工具是Cucumber,它允许我们使用自然语言风格的描述来编写测试用例。以一个iOS应用为例,我们可以这样进行BDD实践:

  1. 用自然语言描述一个功能的行为(例如,登录功能)。
  2. 编写对应的步骤定义(Step Definitions),这些步骤定义用来链接到实际的业务逻辑代码。
  3. 编写测试代码以确保步骤定义的执行。
  4. 编写业务逻辑代码直到所有的步骤定义都满足。
Feature: Login Feature

  Scenario: Successful login
    Given the user is on the login screen
    When the user enters valid credentials
    And clicks on the login button
    Then the user should be redirected to the home screen

在这个例子中,我们使用Gherkin语言来编写一个用户登录功能的行为描述。该描述作为业务人员、开发人员和测试人员之间的共同理解基础。开发团队接下来将编写步骤定义和业务逻辑代码以满足该行为描述。

接下来,这个行为描述可以被转换为具体的测试代码,以确保软件的行为符合预期。

总的来说,TDD和BDD都是提高软件质量和生产力的有效实践,它们通过编写测试用例来引导开发,最终实现高质量的软件。尽管TDD侧重于测试,而BDD侧重于业务行为,但两者都可以在Xcode中实现,并与

 XCTest 

框架无缝集成。

6.

 XCTest 

框架的配置、运行和生命周期

6.1

 XCTest 

的配置细节

6.1.1 测试环境的搭建

 XCTest 

框架是Xcode内置的单元测试框架,为iOS/macOS应用提供了一套完整的测试解决方案。搭建

 XCTest 

测试环境相对简单,遵循以下步骤:

  1. 打开Xcode并选择你的项目。
  2. 创建一个新的测试目标,通常是在项目的目标列表中选择“Add Target”按钮,然后选择“iOS Unit Testing Bundle”或“macOS Unit Testing Bundle”。
  3. 命名你的测试目标,并确保“include Unit Tests”选项被选中,这将自动创建一个包含测试用例模板的测试target。
  4. 在项目导航器中,找到你的测试target下的“ Tests”文件夹,这里存放着所有的测试用例文件。

一旦测试环境搭建完成,你可以开始编写测试用例并在Xcode中直接运行它们。

6.1.2 测试参数的配置

在Xcode中,你可以对测试行为进行配置,包括环境变量、命令行参数等。通过以下方法配置:

  1. 在Xcode中,选择“Product”菜单下的“Scheme”>“Edit Scheme…”。
  2. 选择“Test”左侧的菜单项,然后在右侧选择“Arguments”标签页。
  3. 在这里,你可以设置“Arguments Passed On Launch”来传递命令行参数,设置“Environment Variables”来配置环境变量。

下面是一个配置示例,演示如何添加环境变量和传递参数:

// 环境变量
NAME = "XCTest"

// 命令行参数
-username "TestUser"

这些配置对测试用例的执行至关重要,尤其是在需要模拟特定测试环境时。

6.2

 XCTest 

的运行机制和生命周期

6.2.1 测试执行流程

 XCTest 

的测试执行流程遵循典型的单元测试框架的生命周期模式。在Xcode中,每个测试用例被组织到测试类中,测试类继承自

 XCTestCase 

。测试执行流程大致如下:

  1. ** 测试套件的初始化 ( setUp ) **
  2. 在测试套件中的第一个测试之前调用。
  3. 通常用于准备测试环境,如创建对象、设置全局变量等。
  4. ** 测试用例的初始化 ( setUp ) **
  5. 在每个测试用例执行之前调用。
  6. 用于重置测试状态,确保测试用例独立运行。
  7. ** 测试方法执行 **
  8. 测试用例的主体,测试方法通常以 test 前缀命名。
  9. 测试方法内可以使用 XCTest 提供的断言方法。
  10. ** 测试用例的清理 ( tearDown ) **
  11. 在每个测试用例执行之后调用。
  12. 用于清除测试用例中的临时资源或状态。
  13. ** 测试套件的清理 ( tearDown ) **
  14. 在测试套件中最后一个测试之后调用。
  15. 清理全局资源,恢复测试前的状态。

每个测试方法都是独立执行的,这意味着每个测试之前都会调用

 setUp 

方法,测试之后都会调用

 tearDown 

方法。

6.2.2 测试生命周期回调方法

 XCTest 

框架提供了多个生命周期回调方法,用于在测试的不同阶段执行特定的代码。下面是一些关键的生命周期回调方法:

  • setUp() : 在每个测试用例执行之前调用,用于设置测试的初始状态。
  • tearDown() : 在每个测试用例执行之后调用,用于清理测试用例执行后的环境。
  • setUpBeforeClass() : 在测试类的所有测试方法开始之前调用一次,用于执行一些只初始化一次的准备任务。
  • tearDownAfterClass() : 在测试类的所有测试方法结束后调用一次,用于执行一些只清理一次的清理任务。
  • setUpExpectingFailure() : 在某个测试预期失败时使用,可以帮助开发者关注测试失败的情况。
  • teardownExpectingFailure() : 和 setUpExpectingFailure() 配套使用,用于清理测试预期失败的环境。

下面是一个简单的代码示例,演示了如何使用

 setUp 

 tearDown 

方法:

class MyTests: XCTestCase {
    var resource: Resource?

    override func setUp() {
        super.setUp()
        // 每个测试用例之前准备资源
        resource = Resource()
    }

    override func tearDown() {
        // 释放资源
        resource?.cleanup()
        resource = nil
        super.tearDown()
    }
    // 测试方法
    func testExample() {
        // 断言检查
        XCTAssertTrue(2 == 2)
    }
}

在这个例子中,每次执行

 testExample 

测试方法之前,

 setUp 

会被调用,资源

 resource 

会被初始化。测试结束后,

 tearDown 

会被调用以清理资源。

接下来的章节将继续深入探讨XCTest框架的使用,包括断言方法的种类和选择、异常处理的策略等高级话题。

7. 断言方法的使用和异常处理

7.1 断言方法的种类和选择

常见断言方法的介绍

 XCTest 

框架中,断言方法用于验证代码行为是否符合预期。以下是一些常用的断言方法:

  • XCTAssertEqualObjects(_:_:) : 检查两个对象是否相等。
  • XCTAssertTrue(_:_:) : 检查条件是否为真。
  • XCTAssertFalse(_:_:) : 检查条件是否为假。
  • XCTAssertNil(_:_:) : 检查对象是否为nil。
  • XCTAssertNotNil(_:_:) : 检查对象是否不为nil。
  • XCTAssertThrowsError(_:_:) : 检查块中是否抛出错误。
  • XCTAssertNoThrow(_:_:) : 检查块是否不抛出错误。

每个断言方法都接受一个消息参数,用于在测试失败时提供额外的信息。

如何选择合适的断言方法

选择合适的断言方法取决于测试的具体需求。通常,你需要根据被测试的函数或方法的预期行为来决定使用哪种断言。例如,当测试某个函数返回值时,可以使用

 XCTAssertEqualObjects 

来检查返回的对象是否与预期相符。

在实现测试时,尽量让每个测试用例使用单一的断言,这样可以清晰地理解测试的目的。如果需要验证多个条件,可以考虑将它们分解到不同的测试用例中。

7.2 异常处理的策略

异常测试的必要性

在开发过程中,处理异常是非常重要的一环。异常测试可以帮助开发者确保他们的代码能够正确地处理错误情况。通过在单元测试中模拟和测试异常情况,开发者可以提高代码的健壮性和可靠性。

异常处理的最佳实践

 XCTest 

框架中,可以使用

 XCTAssertThrowsError 

 XCTAssertNoThrow 

来测试函数是否会抛出错误或是否不会抛出错误。下面是一个简单的例子来展示如何使用这些断言:

func testExceptionHandling() {
    letラー = NSError(domain: "TestError", code: 101, userInfo: [NSLocalizedDescriptionKey: "An error occurred."])
    // 测试抛出错误的情况
    letラー预期 = 抛出预期(NSError.self)
    来用() {
        有可能抛出错误的函数()
    } 闭合 {
        作为(错误) {
           XCTAssertEqualObjects(错误, 预期错误)
        } 否则 {
            XCTFail("Expected an error, but none was thrown.")
        }
    }
    // 测试不抛出错误的情况
    来用() {
        不抛出错误的函数()
    } 闭合 {
        作为(错误) {
            XCTFail("Expected no error, but got \(错误).")
        } 否则 {
            // 测试成功,没有抛出错误
        }
    }
}

在上述代码中,我们定义了两个测试用例,一个用于测试函数是否会抛出错误,另一个则测试函数是否不会抛出错误。通过这种方式,可以全面地测试代码对异常情况的处理。

单元测试不仅是代码质量的保障,也是开发流程中不可或缺的一部分。通过有效地使用断言和进行异常处理测试,开发者可以构建出更加稳定、可靠的应用程序。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文旨在为iOS和macOS开发者介绍Xcode单元测试的中文版教程,重点讲解如何在Xcode集成开发环境中编写和执行单元测试,以提高软件质量。教程不仅提供了基础概念和XCTest框架的使用方法,还包括断言使用、模拟对象和测试覆盖率等高级主题。同时,也针对初学者提供了入门指南,以及如何查看代码覆盖率报告和进行UI测试。中文版教程有助于降低语言障碍,让开发者能够通过实践来提升代码的稳定性和可靠性。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

标签:

本文转载自: https://blog.csdn.net/weixin_36444661/article/details/142033080
版权归原作者 Suvo Sarkar 所有, 如有侵权,请联系我们删除。

“Xcode单元测试中文教程:入门到精通”的评论:

还没有评论