- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在为一个白标项目编写 UI 测试,其中每个应用程序都有一组不同的菜单项。测试点击每个菜单项并截取屏幕截图(使用 fastlane snapshot )。
目前这一切都发生在一个名为 testScreenshotAllMenuItems()
的 XCTestCase
中,如下所示:
func testScreenshotAllMenuItems() {
// Take a screenshot of the menu
openTheMenu()
snapshot("Menu")
var cells:[XCUIElement] = []
// Store each menu item for use later
for i in 0..<app.tables.cells.count {
cells.append(app.tables.cells.element(boundBy: i))
}
// Loop through each menu item
for menuItem in cells.enumerated() {
let exists = menuItem.element.waitForExistence(timeout: 5)
if exists && menuItem.element.isHittable {
// Only tap on the menu item if it isn't an external link
let externalLink = menuItem.element.children(matching: .image)["external link"]
if !externalLink.exists {
var name = "\(menuItem.offset)"
let cellText = menuItem.element.children(matching: .staticText).firstMatch
if cellText.label != "" {
name += "-\(cellText.label.replacingOccurrences(of: " ", with: "-"))"
}
print("opening \(name)")
menuItem.element.tap()
// Screenshot this view and then re-open the menu
snapshot(name)
openTheMenu()
}
}
}
}
我希望能够动态生成每个屏幕截图,因为它是自己的测试用例,以便将这些屏幕截图正确地报告为单独的测试,可能是这样的:
[T] Screenshots
[t] testFavouritesViewScreenShot() ✓
[t] testGiveFeedbackViewScreenShot() ✓
[t] testSettingsViewScreenShot() ✓
我看过关于 creating tests programmatically 的文档但我不确定如何快速设置它。 - 理想情况下,我会使用闭包将现有的屏幕截图测试包装到它们自己的 XCTestCase
中 - 我想象如下,但似乎没有任何有用的初始化方法来实现这一点:
for menuItem in cells {
let test = XCTestCase(closure: {
menuItem.tap()
snapshot("menuItemName")
})
test.run()
}
我不理解文档建议使用的调用和选择器的组合,我找不到任何好的示例,请为我指明正确的方向,或者分享您拥有的有关此工作的任何示例。
最佳答案
自 NSInvocation
以来,您可能无法在纯 swift 中完成此操作不再是 swift api 的一部分。
XCTest依赖+ (NSArray<NSInvocation *> *)testInvocations
获取一个内部测试方法列表的函数 XCTestCase
类(class)。您可以假设的默认实现只是找到所有以 test
开头的方法前缀并将它们包裹在 NSInvocation
中返回. (您可以阅读更多关于 NSInvocation
here 的信息)
所以如果我们想在运行时声明测试,这就是我们感兴趣的地方。
遗憾NSInvocation
不再是 swift api 的一部分,我们无法覆盖此方法。
如果您可以使用一点点 ObjC,那么我们可以创建隐藏 NSInvocation 细节的父类(super class),并为子类提供快速友好的 api。
/// Parent.h
/// SEL is just pointer on C struct so we cannot put it inside of NSArray.
/// Instead we use this class as wrapper.
@interface _QuickSelectorWrapper : NSObject
- (instancetype)initWithSelector:(SEL)selector;
@end
@interface ParametrizedTestCase : XCTestCase
/// List of test methods to call. By default return nothing
+ (NSArray<_QuickSelectorWrapper *> *)_qck_testMethodSelectors;
@end
/// Parent.m
#include "Parent.h"
@interface _QuickSelectorWrapper ()
@property(nonatomic, assign) SEL selector;
@end
@implementation _QuickSelectorWrapper
- (instancetype)initWithSelector:(SEL)selector {
self = [super init];
_selector = selector;
return self;
}
@end
@implementation ParametrizedTestCase
+ (NSArray<NSInvocation *> *)testInvocations {
// here we take list of test selectors from subclass
NSArray<_QuickSelectorWrapper *> *wrappers = [self _qck_testMethodSelectors];
NSMutableArray<NSInvocation *> *invocations = [NSMutableArray arrayWithCapacity:wrappers.count];
// And wrap them in NSInvocation as XCTest api require
for (_QuickSelectorWrapper *wrapper in wrappers) {
SEL selector = wrapper.selector;
NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;
[invocations addObject:invocation];
}
/// If you want to mix parametrized test with normal `test_something` then you need to call super and append his invocations as well.
/// Otherwise `test`-prefixed methods will be ignored
return invocations;
}
+ (NSArray<_QuickSelectorWrapper *> *)_qck_testMethodSelectors {
return @[];
}
@end
所以现在我们的 swift 测试类只需要从这个类继承并覆盖 _qck_testMethodSelectors
:
/// RuntimeTests.swift
class RuntimeTests: ParametrizedTestCase {
/// This is our parametrized method. For this example it just print out parameter value
func p(_ s: String) {
print("Magic: \(s)")
}
override class func _qck_testMethodSelectors() -> [_QuickSelectorWrapper] {
/// For this example we create 3 runtime tests "test_a", "test_b" and "test_c" with corresponding parameter
return ["a", "b", "c"].map { parameter in
/// first we wrap our test method in block that takes TestCase instance
let block: @convention(block) (RuntimeTests) -> Void = { $0.p(parameter) }
/// with help of ObjC runtime we add new test method to class
let implementation = imp_implementationWithBlock(block)
let selectorName = "test_\(parameter)"
let selector = NSSelectorFromString(selectorName)
class_addMethod(self, selector, implementation, "v@:")
/// and return wrapped selector on new created method
return _QuickSelectorWrapper(selector: selector)
}
}
}
预期输出:
Test Suite 'RuntimeTests' started at 2019-03-17 06:09:24.150
Test Case '-[ProtocolUnitTests.RuntimeTests test_a]' started.
Magic: a
Test Case '-[ProtocolUnitTests.RuntimeTests test_a]' passed (0.006 seconds).
Test Case '-[ProtocolUnitTests.RuntimeTests test_b]' started.
Magic: b
Test Case '-[ProtocolUnitTests.RuntimeTests test_b]' passed (0.001 seconds).
Test Case '-[ProtocolUnitTests.RuntimeTests test_c]' started.
Magic: c
Test Case '-[ProtocolUnitTests.RuntimeTests test_c]' passed (0.001 seconds).
Test Suite 'RuntimeTests' passed at 2019-03-17 06:09:24.159.
感谢 Quick 团队的 super class implementation .
编辑:我用示例创建了 repo github
关于ios - 如何动态添加 XCTestCase,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55142213/
我有两个 XCTestCase 子类, @interface SessionTest : XCTestCase @interface UserTest : XCTestCase 在 SessionTe
我在 XCTestCase 类中有几个测试用例,例如测试 1、测试 2 等我只想在 testPrecondition 通过时运行 test1, test2。我怎样才能做到这一点? 最佳答案 您必须覆盖
我在场景中有 XCTestCase 在XCTestCase 主体中,有一组测试用例。我只希望所有测试用例在第一个测试用例成功完成后开始的问题(在我的特殊目的中,第一个测试用例响应一个 token 供其
我正在为我的一个模型进行单元测试,该模型使用对我的 rest api 的异步调用。用于请求我的 API 的方法是这样的: requestOnComplete:(void(^)())complete o
我有一个 XCTestCase,但如果测试失败 - 应用会立即退出。我确实将此行更改为以下内容: continueAfterFailure = true 失败后它仍然退出应用程序。有谁知道如何在 UI
我需要测试一个 UIViewController,它的行为取决于给它的参数(控件是根据网络服务调用在 viewDidLoad 中动态实例化的)。 我将能够运行相同的 XCTestCase 派生类并注入
不知何故,我的 XCtestCase 类无法访问我项目中的类。是否有一种方法来检查测试文件是否与项目链接?如果是,在哪里检查? 最佳答案 选择要在测试用例中导入或使用的文件。在 Identity In
我正在编写一个在线和离线功能分离的应用程序。在这些功能中,我使用 Reachability 来检查互联网连接,并且在每种情况下(在线/离线),它都会执行不同的工作。 现在,我被要求为这些业务逻辑编写测
我正在使用 XCode 的 XCTestCase 进行自动化 UI 测试,以衡量我的应用程序的性能。我目前有一个包含 25 000 个元素的 UITable,当尝试运行应该滑动此列表的测试时,它会一直
我正在为一个白标项目编写 UI 测试,其中每个应用程序都有一组不同的菜单项。测试点击每个菜单项并截取屏幕截图(使用 fastlane snapshot )。 目前这一切都发生在一个名为 testScr
为什么我的可选实例变量是 nil,而实际上我将它设置为非 nil? 代码: class FooTests: XCTestCase { var foo: Int? func test_A
我的 UI 测试失败了,因为测试会无休止地等待,直到应用空闲。我看不到后台有任何事情发生,例如加载微调器。 它只出现在一个选项卡上。所有其他选项卡均可点击,但屏幕 3 上的测试失败。我在屏幕 3 上捕
我正在尝试测试一段代码,我在其中检查一个帐户是否已经创建了一个 key 并将其存储在钥匙串(keychain)中。如果不是,它会调用启动 oauth 进程的方法。 我的第一个想法是覆盖我想在用户没有
无法使用 xcodebuild test -scheme DollarTests -project Dollar.xcodeproj -configuration Debug -sdk macosx
我正在尝试为使用 Web 服务 API 的 iOS 客户端编写一些单元测试。我想使用实际服务而不是模拟数据。 在运行所有测试之前,我需要获取一个 token 来对用户进行身份验证。我正在尝试使用类 s
背景 以下测试调用 XCTestCase 扩展的方法。目标: waitForElementExists 方法因元素存在而返回,或者 waitForElementExists 方法调用它的测试用例/se
在 XCTestCase 期间,我想以某种方式说“禁用互联网”,它就会被有效地禁用。然后我想以某种方式说“启用互联网”,它就会回来。关键是要看到我的应用程序在网络请求失败然后成功时表现正常。做这个的最
我调用了一个 Restful API。我需要等待调用完成才能验证结果。很常见的问题,但我找不到关于如何将其合并到 XCTestCase 中的合适答案。我已经看到信号量、锁、for 循环都弄乱了代码来运
我在 xcode 中遇到了一些奇怪的问题。 我有一个项目,我决定添加一些单元测试。 我创建了新的测试包并将我的应用添加为测试目标。只是认为我更改为自动生成的代码是: Build Settings /
我正在尝试对 UIViewController 中的按钮点击接线进行单元测试,但我发现即使正在运行的应用程序中的代码工作正常,这些测试也会失败。 我通过删除 View Controller 简化了失败
我是一名优秀的程序员,十分优秀!