Xcode XCTest测试中从情节提要加载的视图控制器中的空UIView快照(间歇性)

Xcode XCTest测试中从情节提要加载的视图控制器中的空UIView快照(间歇性),xcode,uiview,uistoryboard,Xcode,Uiview,Uistoryboard,我们一直在跟踪我们的项目,在该项目中,快照测试用例会间歇性失败。我们方法的要点是渲染视图控制器的视图,并将该图像与参考图像进行比较,看看它们是否不同。我们的方法有几个层次: 我们的问题是,有时渲染视图创建的图像是空的。只是一个大(正确大小)的透明图像 我对每一个都进行了单独测试,并确定这些都不是问题所在。相反,我已经能够在一个独立的普通Xcode项目中重现这一点 通过使用FBSnapshotTestCases用来渲染视图的方法,我创建了一个简单的测试。要复制,请创建“主细节”模板的新项

我们一直在跟踪我们的项目,在该项目中,快照测试用例会间歇性失败。我们方法的要点是渲染视图控制器的视图,并将该图像与参考图像进行比较,看看它们是否不同。我们的方法有几个层次:

我们的问题是,有时渲染视图创建的图像是空的。只是一个大(正确大小)的透明图像

我对每一个都进行了单独测试,并确定这些都不是问题所在。相反,我已经能够在一个独立的普通Xcode项目中重现这一点

通过使用FBSnapshotTestCases用来渲染视图的方法,我创建了一个简单的测试。要复制,请创建“主细节”模板的新项目,并为细节视图控制器提供“细节”的情节提要ID。然后创建这个简单的单元测试

func testExample1() {
    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
    let sut = storyboard.instantiateViewControllerWithIdentifier("Detail") as UIViewController

    sut.beginAppearanceTransition(true, animated: false)
    sut.endAppearanceTransition()

    UIGraphicsBeginImageContextWithOptions(sut.view.frame.size, false, 0)
    sut.view.drawViewHierarchyInRect(sut.view.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    let data = UIImagePNGRepresentation(image)

    println("byte length: \(data.length)")
}
没什么特别的,它很可能会过去。但是,如果再重复几次代码:

func testExample1() { ... }
func testExample2() { ... }
func testExample3() { ... }
输出非常奇怪(被截断):

字节长度应该相同,但不相同。第二个测试(有时是第三个测试)将有一个空视图,就像我们的问题一样

演示该问题的示例项目可用

我能够使用Objective-C测试项目重现这个问题,所以这不太可能是一个快速的问题。在过去的项目中,我们没有为视图控制器UI使用故事板,因此可能需要额外的步骤来“强制”加载视图。这也可能是Xcode 6.x或iOS 8的问题(我重复了Xcode 6.0.1的问题)


有没有人遇到过这样的问题,从情节提要加载的控制器的视图的渲染图像是透明的?

通过切换到生成的UIView,将情节提要从等式中去掉:

let view = UIView(frame: CGRectMake(0, 0, 300, 300))
view.backgroundColor = UIColor.blueColor()

UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

let data = UIImagePNGRepresentation(image)

println("byte length: \(data.length)")
给出了类似的结果

Test Case '-[TestTestTests.TestTestTests testExample1]' started.
byte length: 9663
Test Case '-[TestTestTests.TestTestTests testExample1]' passed (1.000 seconds).
Test Case '-[TestTestTests.TestTestTests testExample2]' started.
byte length: 9663
Test Case '-[TestTestTests.TestTestTests testExample2]' passed (0.112 seconds).
Test Case '-[TestTestTests.TestTestTests testExample3]' started.
byte length: 6469

似乎有办法

    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
    let sut = storyboard.instantiateViewControllerWithIdentifier("Detail") as UIViewController

    sut.beginAppearanceTransition(true, animated: false)
    sut.endAppearanceTransition()

    UIGraphicsBeginImageContextWithOptions(sut.view.frame.size, false, 0)
    let context = UIGraphicsGetCurrentContext
    sut.view.layer.renderInContext(context())
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    let data = UIImagePNGRepresentation(image)
    println("byte length: \(data.length)")

本主题建议您尝试使用“[self.view.layer renderInContext:UIGraphicsGetCurrentContext()]”代替:


如果有人感兴趣,使用渲染视图的方法不会产生问题。我可能不得不打开一个雷达,在Facebook的图书馆里发布一个问题。这几乎肯定是UIKit中的一个bug。同时,我正在使用FBSnapshotTestCase(详细信息)的分支。我将提交一个雷达文件,并发布一个指向openradar的链接。对于在家中跟随我的任何人,我已经打开了一个描述这个问题的链接。如果我听到回音,我会在那里更新。是的,当然。这实际上是Facebook lib用来做的。有一种方法可以提供使用它的选项。希望它能很快被合并。是的,这就是Facebook的图书馆过去所做的。与此同时,我又恢复了那种老习惯。
    let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
    let sut = storyboard.instantiateViewControllerWithIdentifier("Detail") as UIViewController

    sut.beginAppearanceTransition(true, animated: false)
    sut.endAppearanceTransition()

    UIGraphicsBeginImageContextWithOptions(sut.view.frame.size, false, 0)
    let context = UIGraphicsGetCurrentContext
    sut.view.layer.renderInContext(context())
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    let data = UIImagePNGRepresentation(image)
    println("byte length: \(data.length)")