ADLayoutTest 1.0.1

ADLayoutTest 1.0.1

CI FabernovelPierre FelginesClaire Peyron 维护。



 
依赖项
ADAssertLayout~> 1.0
SwiftCheck~> 0.12
 

  • Pierre Felgines

ADLayoutTest

Version License Platform

ADLayoutTest 是对 UI 布局基于属性的测试实现的示例。

主要思路是生成随机关联视图输入(使用 SwiftCheck),布局视图,然后断言一些布局属性是真(使用 ADAssertLayout)。

我们可以利用同样的思路制作快照测试。

特点

示例

您可以在 示例 目录中找到使用示例。

假设我们在应用程序中使用了以下视图,并希望对其进行测试

import Foundation
import UIKit

struct ExampleViewModel {
    let image: UIImage?
    let text: String
    let subText: String
}

class ExampleView: UIView {

    @IBOutlet var imageView: UIImageView!
    @IBOutlet var label: UILabel!
    @IBOutlet var subLabel: UILabel!

    // MARK: - Public

    func configure(with viewModel: ExampleViewModel) {
        imageView.image = viewModel.image
        label.text = viewModel.text
        subLabel.text = viewModel.subText
    }
}

我们希望生成随机视图模型以配置视图。为此,我们使用 SwiftCheck 使 ExampleViewModel 符合 Arbitrary。这样我们就有了一个 ExampleViewModel 的随机生成器。

import SwiftCheck

extension ExampleViewModel: Arbitrary {

    // This is a random generator of `ExampleViewModel`
    public static var arbitrary: Gen<ExampleViewModel> {
        return Gen<ExampleViewModel>.compose { c in
            return ExampleViewModel(
                image: c.generate(using: .image(min: 20, max: 100)),
                text: c.generate(using: .words),
                subText: c.generate(using: .words)
            )
        }
    }
}

.image(min: 20, max: 100).words 是创建随机图片或字符串实例的 自定义 生成器。

一个基本的测试用例用于验证 ExampleView 布局是

import XCTest
import ADAssertLayout
import ADLayoutTest

class ADLayoutTest_ExampleTests: XCTestCase {

    func testExampleView() {

        runLayoutTests(named: "ExampleView") { (viewModel: ExampleViewModel) in
            // create the view we want to test
            let view: ExampleView = ExampleView.fromNib(named: "ExampleViewValid")

            // setup the view and pass the random view model
            view.backgroundColor = UIColor.white
            view.frame = CGRect(x: 0, y: 0, width: 320.0, height: 150.0)
            view.configure(with: viewModel)

            // layout the view
            view.setNeedsLayout()
            view.layoutIfNeeded()

            // run layout assertions (no view overlap, no ambiguous layout, etc)
            do {
                try view.ad_runBasicRecursiveTests()
            } catch {
                return .failure(view, error)
            }
            return .success(view)
        }
    }
}

如果有错误发生,您可以在日志中找到原因。

Test Case '-[ADLayoutTest_ExampleTests.ADLayoutTest_ExampleTests testExampleViewError]' started.
/Users/felginep/Sources/ADLayoutTest/Example/ADLayoutTest_ExampleTests/ADLayoutTest_ExampleTests.swift:83: error: -[ADLayoutTest_ExampleTests.ADLayoutTest_ExampleTests testExampleViewError] : failed - Bottom right corner of <UIImageView: 0x7f8af7c293e0; frame = (20 20; 70 70); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x60000220cd20>> (superview: <ADLayoutTest_Example.ExampleView: 0x7f8af7c291e0; frame = (0 0; 320 150); autoresize = W+H; layer = <CALayer: 0x60000220ce00>>) overlaps upper left corner of <UILabel: 0x7f8af7c29610; frame = (62 20; 238 20.5); text = 'Erat porta eget venenatis...'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600000159770>> (superview: <ADLayoutTest_Example.ExampleView: 0x7f8af7c291e0; frame = (0 0; 320 150); autoresize = W+H; layer = <CALayer: 0x60000220ce00>>).
*** Failed! Proposition: ExampleView Overlap
Falsifiable (after 9 tests):
ExampleViewModel(image: Optional(<UIImage: 0x6000009a4380>, {36, 79}), text: "Erat porta eget venenatis porta", subText: "Elit elit etiam dapibus at erat")
*** Passed 8 tests
.
error: -[ADLayoutTest_ExampleTests.ADLayoutTest_ExampleTests testExampleViewError] : failed - Falsifiable; Replay with 904979560 363827163 and size 8

请注意,您可以看到哪个输入导致了布局错误

ExampleViewModel(
    image: Optional(<UIImage: 0x6000009a4380>, {36, 79}),
    text: "Erat porta eget venenatis porta",
    subText: "Elit elit etiam dapibus at erat"
)

如果您想为调试目的重现相同的错误,您可以使用日志消息来使用自定义随机策略 .replay 重放测试。

Falsifiable; Replay with 904979560 363827163 and size 8

然后参数是

.replay(
    seed1: 904979560,
    seed2: 363827163,
    size: 8
)

如果您进入测试报告,您可以在其中有布局错误的失败视图的快照。

Test Report Failure Failure Overlap Highlighted

参数

可以向 runLayoutTests 函数传递一些参数

  • snapshotStrategy:默认情况下,当测试失败时,使用 .failureOnly,只捕获失败的快照并将其显示在测试报告中。如果您想为每个输入捕获每个视图的快照,可以传递 .allTests 参数。
  • randomStrategy:默认情况下,使用 random 生成真实随机值(这意味着两次不同的测试执行不会有相同的视图模型)。如果您想获得一致的输入,使用 .consistently.replay 参数。
  • maxTestsCount:默认值为 100 个测试。如果需要,您可以降低此值。

返回值

runLayoutTests 函数接受一个闭包参数。此闭包返回一个 ViewAssertionResult。此结果包含两个可选值:一个 view 和一个 error。如果存在,view 会被用来截图并显示在测试报告中。如果返回了 error,则测试失败。

快照测试

您可以使用相同的技术创建快照测试,并断言在开发过程中视图的外观不会改变。为此,导入快照测试库(在示例中我们使用 SnapshotTesting),并使用具有 .consistently 随机策略的 runLayoutTests 函数来生成始终相同的随机值。

    func testExampleViewSavedScreenshots() {
        runLayoutTests(
            named: "ExampleView Saved Screenshots",
            randomStrategy: .consistent, // mandatory to have the same screenshots every time
            maxTestsCount: 5 // we only want 5 screenshots
        ) { (viewModel: ExampleViewModel) in
            // same code as before
            let view: ExampleView = ExampleView.fromNib(named: "ExampleViewValid")
            view.backgroundColor = UIColor.white
            view.frame = CGRect(x: 0, y: 0, width: 320.0, height: 150.0)
            view.configure(with: viewModel)
            view.setNeedsLayout()
            view.layoutIfNeeded()

            // Snapshot the view (depends of the library used)
            assertSnapshot(
                matching: view,
                as: .image
            )

            // no layout assertions, we just check the generated screenshots
            return .success // we don't pass the view here because we don't care
        }
    }

内置生成器

您可以在其中找到一些有用的预定义生成器,您可以用它来创建随机输入。

请随意利用 Gen 类型来创建自己的生成器。您可以在 SwiftCheck 页面找到所有文档。

需求

  • iOS 9.0+
  • Swift 5.1

安装

CocoaPods

CocoaPods 是 Cocoa 项目的依赖管理器。您可以使用以下命令安装它

$ gem install cocoapods

要使用 CocoaPods 将 ADLayoutTest 集成到您的 Xcode 项目中,请在您的 Podfile 中指定它

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '12.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'ADLayoutTest'
end

然后,运行以下命令

$ pod install

通讯

  • 如果您 需要帮助,请使用 Twitter
  • 如果您想 提出一般性问题,请使用 Twitter
  • 如果您想 申请工作,请访问 https://careers.fabernovel.com/
  • 如果您 发现了错误,请打开一个 issue。
  • 如果您 有功能请求,请打开一个 issue。
  • 如果您 想做出贡献,提交一个 pull request。

鸣谢

ADLayoutTest 由 Fabernovel 拥有和维护。您可以在 @FabernovelTech 上关注我们。

许可证

ADLayoutTest 在MIT许可证下发布。更多信息请参阅LICENSE