MixboxDi 0.3.46

MixboxDi 0.3.46

由以下人员维护:Artyom RazinovAlexey ShpirkoArtyom RazinovVladislav AlekseevTimofey Solonin



MixboxDi 0.3.46

  • Avito 旗下的程序员社区

概述

Version License Build Status

强大的 iOS 端到端 UI 测试框架。

目前它在 Avito 中使用,我们拥有 700+ 个端到端 UI 测试用例,大约 90% 的测试用例结果为绿色,并且这些测试用例帮助我们已经减少了 2 年的手动测试工作。大约 25% 的测试在 PR 中运行,我们正在努力使所有测试用例在 pull request 中执行,部分是通过将从黑盒端到端测试转换为灰盒测试来实现。我们在 3 个平台上运行这些测试,大约需要 40 分钟(总共需 30+ 小时),因为我们使用了 Emcee,这是一个可以在多台机器上运行测试的测试执行器(需要注意的是 Mixbox 不需要 Emcee)。我们还在编写灰盒测试(其中模拟类、网络和所有内容),但我们才刚开始。

如果您对在您公司中使用它感到兴奋,请给我们提交一个问题。我们正在努力使其可供社区使用,但这不是我们的主要目标。

特性

  • 操作和检查(显而易见)

  • 像素可视性检查

  • 一切都有轮询功能

  • 完全自动的滚动

  • 黑盒和灰盒测试!

    • 黑盒测试在单独的应用程序中运行,能够启动应用。您可以使用这种类型的测试测试更多应用功能。您可以使用模拟,但这将需要您编写更多代码(使用启动参数或实现进程间通信)。
    • 灰盒测试在应用程序内部运行(例如EarlGrey),但测试与黑盒测试兼容,因此您可以共享测试模型、页面对象、测试助手等。由于测试在相同进程中执行,您可以轻松地模拟任何事物。您必须在不需要模拟的情况下测试应用启动。

    大多数代码在灰盒测试和黑盒测试之间共享,因为大多数功能都有其自身的优点。使用两种方法可以获得良好的测试金字塔

  • 页面对象

    • 内部可以包含任何代码的函数
    • 页面对象元素可以嵌套元素(不过,可能需要一些不太美观的模板代码)
    • 所有功能(动作/检查)都是完全可扩展的。如果您实现自定义动作或检查,它将自动适用于黑盒和灰盒测试(例如查看SwipeAction,所有内置动作实际上都是扩展)
  • UICollectionView中的每个单元格在测试中都是可见的(包括离屏单元格)

  • 可自定义的应用与测试之间的进程间通信

  • 在测试中可见的视图的可定制值

  • 网络模拟(通过NSURLSessionProtocol)

  • 设置权限(相机/地理位置/通知等)

  • 推送通知的模拟(限制:仅在活动应用内部!)

  • 从测试中打开URL

  • 地理位置模拟

  • 硬件键盘(定义了非常少的键码,但它可以轻松实现)

  • 无需分叉存储库即可进行自定义

  • Swift & Objective-C

  • 已测试

    • 在3种设备配置上设置了176个黑盒UI测试
    • 在3种设备配置上设置了155个灰盒UI测试
    • 在4种设备配置上设置了100个单元测试
    • SwiftLint +自定义linter
    • 所有测试都在每次拉动请求到Mixbox时执行,通常每个PR等于1次提交。
    • 两个演示使用5个版本的Xcode(10.0、10.1、10.2.1、10.3、11.0)进行了测试。
  • 可配置的报告(例如:Tests项目与Allure集成,它是一个开源的具有web UI的报告系统,在Avito中我们使用内部解决方案进行报告;您可以编写自己的实现)

处于开发状态/尚未开源

  • 页面对象的代码生成
  • 获取应用程序中所有断言失败
  • Springboard操作的包装器
  • 在发布和测试构建之间切换辅助功能值

安装

Mixbox有两种用法。

第一种在演示中描述,被简化了,基本上只是使用pod SomePod

第二种我们在Avito中使用,看起来是这样的:测试(在那里查看Podfile)。

文档还不够充分,因此您可以尝试使用简单的Mixbox链接方法(演示),但请使用测试中的代码示例。

支持的iOS/Xcode/Swift版本

  • Xcode 11
  • Swift 5
  • iOS 10.3, iOS 11.4, iOS 12.1, 中间版本可能工作或不工作,提到的版本已在CI上进行测试
  • Cocoapods 1.8.4
  • Mac OS 10.14.6

Xcode 9/10及更旧版本不再受支持。如果您计划在不同的环境中使用项目并遇到问题,请告知我们。

已知问题

  • 在iOS 11.2上崩溃(在iOS 11.3和iOS 11.4上工作正常)
  • 在物理设备上设置权限不工作(可能还有其他问题,我们没有在物理设备上测试;基本功能可以工作)
  • 没有测试设备旋转,我认为我们在这方面有一些错误
  • 未测试iPad
  • 报告中出现俄语(将很快修复)

示例

有关实际示例,请参阅Tests项目。这是如何使用的最新开源示例,但它缺乏现实性(它没有显示如何为真实应用编写测试)。

展示基本特性的测试示例

func test() {
    // Setting permissions
    permissions.camera.set(.allowed)
    permissions.photos.set(.notDetermined)

    // Functions are useful in page objects and allows
    // reusing code, for example, for transitions between states of the app
    pageObjects.initial
        .authorize(user: testUser)
        .goToCvScreen()
        
    // Aliases for simple assertions (you can add your own):
    pageObjects.simpleCv.view.assertIsDisplayed()
    pageObjects.simpleCv.title.assertHasText("My CV")
    
    // Fully customizable assertions
    pageObjects.simpleCv.addressField.assertMatches { element in
        element.text != addressFieldInitialText && element.text.isNotEmpty
    }
    
    // Network stubbing.
    networking.stubbing
        .stub(urlPattern: ".*?example.com/api/cv")
        .thenReturn(file: "cv.json")
    // There is also a monitoring feature, including recording+replaying feature that
    // allows to record all network and replay in later, so your tests will not require internet.
    
    // Actions
    pageObjects.simpleCV.occupationField.setText("iOS developer")
    pageObjects.simpleCV.createCVButton.tap()
}

声明页面对象

public final class MapScreen:
    BasePageObjectWithDefaultInitializer,
    ScreenWithNavigationBar // protocol extensions are very useful for sharing code
{
    // Basic locator  
    public var mapView: ViewElement {
        return element("Map view") { element in
            element.id == "GoogleMapView"
        }
    }
    
    // You can use complex checks
    // Note that you can create your own matchers like `element.isFooBar()`
    public func pin(coordinates: Coordinates, deltaInMeters: Double = 10) -> ViewElement {
        return element("Pin with coordinates \(coordinates)") { element in
            element.id == "pin" && element
                .customValues["coordinates", Coordinates.self]
                .isClose(to: coordinates, deltaInMeters: deltaInMeters)
        }
    }
}

声明自定义页面对象元素

public final class RatingStarsElement:
    BaseElementWithDefaultInitializer,
    ElementWithUi
{
    public func assertRatingEqualsTo(
        _ number: Int,
        file: StaticString = #file,
        line: UInt = #line)
    {
        assertMatches(file: file, line: line) { element in
            element.customValues["rating"] == number
        }
    }
}

复制的代码

此库包含从其他库复制的某些代码。有时是一个单个文件,有时是因为我们需要在源中构建条件编译来防止在发布版本中链接代码。

用于测试

其他文档