【问题标题】:How to Get Test Coverage for Guard Statement Fall-through如何获得 Guard Statement Fall-through 的测试覆盖率
【发布时间】:2017-01-01 22:40:55
【问题描述】:

我今天开始使用 BDD 方法编写 iOS 单元测试。我有一个关于 guard 语句和达到 100% 代码覆盖率的问题。

我有以下代码,它处理将Data 转换为Customer 对象。

internal final class func customer(from data: Data) -> Customer? {
    do {
        guard let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, Any> else {
            return nil
        }
        var customerFirstName: String? = nil
        var customerLastName: String
        if let firstName = jsonDictionary["first_name"] as? String {
            customerFirstName = firstName
        }
        guard let lastName = jsonDictionary["last_name"] as? String else {
            return nil
        }
        customerLastName = lastName
        return Customer(firstName: customerFirstName, lastName: customerLastName)
    } catch {
        return nil
    }
}

创建我们的后端时,一些客户只获得了一个姓氏,其中包含他们的名字和姓氏。这就是为什么客户的名字是可选的;他们的全名可能是last_name 的值。

在我的代码中,客户的名字是可选的,而他们的姓氏是必需的。如果从网络请求收到的 JSON 中没有返回他们的姓氏,那么我不会创建客户。此外,如果Data 不能序列化为Dictionary,则不会创建客户。

我有两个 JSON 文件,它们都包含我用来测试这两个场景的客户信息。

一个在 JSON 中不包含名字:

{
    "first_name": null,
    "last_name": "Test Name",
}

另一个包含 JSON 中的名字:

{
    "first_name": "Test",
    "last_name": "Name",
}

在我的单元测试中,我使用 Quick 和 Nimble,在名字不可用和可用时处理 Customer 的创建:

override func spec() {
    super.spec()
    let bundle = Bundle(for: type(of: self))
    describe("customer") {
        context("whenAllDataAvailable") {
            it("createsSuccessfully") {
                let path = bundle.path(forResource: "CustomerValidFullName", ofType: "json", inDirectory: "ResponseStubs")!
                let url = URL(fileURLWithPath: path)
                let data = try! Data(contentsOf: url)
                let customer = DataTransformer.customer(from: data)
                expect(customer).toNot(beNil())
            }
        }
        context("whenMissingLastName") {
            it("createsUnsuccessfully") {
                let path = bundle.path(forResource: "CustomerMissingLastName", ofType: "json", inDirectory: "ResponseStubs")!
                let url = URL(fileURLWithPath: path)
                let data = try! Data(contentsOf: url)
                let customer = DataTransformer.customer(from: data)
                expect(customer).to(beNil())
            }
        }
    }
}

这可确保我在返回的 JSON 中缺少或存在名字时创建 Customer

当我的代码没有命中guard 语句的else 子句时,我如何才能使用BDD 达到100% 的代码覆盖率,因为数据能够转换为有效的JSON 对象?我是否应该添加另一个带有无法转换为 JSON 对象的数据的 .json 文件以确保不创建 Customer 以及包含缺少的 last_name.json 文件以确保 @987654339 @ 没有创建?

我只是在想“100% 代码覆盖率”的概念吗?我什至需要测试guard 语句的else 子句吗?我什至有使用 BDD 方法的适当方法吗?

【问题讨论】:

    标签: swift unit-testing bdd guard-statement


    【解决方案1】:

    只要写出你想要的任何 JSON 格式——你能想到的各种错误格式。例子:

    • 您可以使用不正确的 JSON 来处理异常。
    • 您可以使用 JSON 数组而不是字典来创建您的第一个 guard

    俗话说,你只需要覆盖你想要正确的代码。 ?

    TDD 和 BDD 是相关的。在 TDD 中,您将首先编写一个失败的测试。然后,您将编写尽可能快地通过该测试的代码。最后,您将清理代码以使其更好。看起来您是在事后添加测试。

    顺便说一句,如果您不使用外部文件,而是将 JSON 直接放入您的测试中,您的测试会更加清晰。这是一个截屏视频,展示了我如何对 JSON 转换的开始进行 TDD。截屏视频使用 Objective-C,但原理相同:https://qualitycoding.org/tdd-json-parsing/

    【讨论】:

    • @JonRein 感谢您的洞察力!我可以问一下,您是如何得出我在事后编写测试的结论的?这正是我所做的,但我很好奇是什么让你得出这个结论。我肯定在研究你的概念,希望能得到更好的理解。
    • @NickKohrn 因为当代码是测试驱动时,你不会编写任何不满足测试的代码。所以你最终不会问,“我有一些代码,现在我该如何覆盖它?”
    【解决方案2】:

    使用 if let.

    实现 100% 的代码覆盖率

    有时,强制准备格式错误的对象来强制执行命中guard 语句中使用的returnreturn nil 是不可行的。

    当您集成了第 3 方 SDK 并且在方法中创建了相应的第 3 方对象时,就会出现这种情况。 例如:

    func aMethod() {
       guard let response = 3rdPartyResponse<3rdPartyInput>.createControl(with: .create(with: .someCase)) as? 3rdPartyResponse<3rdPartyInput> else { return }
    }
    

    在这种情况下,它非常困难,有时无法返回。

    但如果代码覆盖率是主要标准,您可以在这种情况下使用 if let 如果让确实给出 100%代码覆盖率

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-12
      • 2019-11-06
      • 2022-11-11
      • 2015-04-01
      • 1970-01-01
      • 2018-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多