如何在单元测试中对UIImage进行断言?最佳方法是什么?

8

假设我正在为视图控制器上的tableView:cellForRowAtIndexPath:代理方法编写单元测试。这个方法根据我传入的索引路径可以返回几种不同的单元格配置。

我可以轻松地断言cell.textLabel.text属性,但如何断言cell.imageView.image属性包含正确的图像?无论是图像还是imageView都没有(公共API)属性可用于查找图像名称或文件名。

我想到的最好方法是创建最小可能的有效.png(在我的单元测试中使用[UIImage imageWithData:]来避免触碰磁盘),并断言从cell.imageView.image获取的字节数组与我期望的一致。我已经创建了一个OCHamcrest匹配器,使这个过程更加美好,但它是一个不令人满意和不灵活的方法。

有没有更好的想法?


图片是从项目中加载还是从网络中加载的? - Grzegorz Krukowski
从这个项目来看,如果我想要对下载的图片进行断言,我会在执行下载操作的类前面放置一个门面,并在测试中模拟它。 - Robert Atkins
4个回答

7
如果您正在使用[UIImage imagedNamed:],那么这些图像是被缓存的。以下是imagedNamed:UIImage类参考中的摘录:

此方法在系统缓存中查找指定名称的图像对象,并返回该对象(如果存在)。如果匹配的图像对象尚不存在于缓存中,则此方法会加载指定文件的图像数据、缓存并返回生成的对象。

这意味着您可以对cell.imageView.image == [UIImage imagedName:@"my_image"]进行断言,因为这是一个指针比较,而且由于这些图像是被缓存的,所以对同一名称进行多次调用imageNamed:将返回相同的指针。即使内存变得紧张,您仍然应该没问题,因为UIImage类参考解释道:

在低内存情况下,图像数据可能会从UIImage对象中清除,以便在系统上释放内存。这种清除行为仅影响UIImage对象内部存储的图像数据,而不影响对象本身。


哇,我没想到。太棒了。唯一让我不立即接受的事情是使用[UIImage imageNamed:]意味着磁盘访问,如果可能的话,我想在我的单元测试中避免这种情况。 - Robert Atkins
如果请求的图像是缓存未命中,那么虽然没有磁盘,但文件系统访问会带来轻微的开销。如果是缓存命中,则是内存读取。 - neilco
这似乎在Xcode 6.4中已经不再起作用了。我不得不使用XCTAssertEqualObjects(image,[UIImage imageNamed:@"myImage"])代替。 - Isaac Overacker

3
将图像转换为数据,然后比较数据。由于图像只是指向内存位置的指针。
guard let data1 = image1?.pngData(), let data2 = image2.pngData() else {
    XCTFail("Data should not be nil")
    return
}
XCTAssertEqual(data1, data2)

swift5


2
虽然这段代码片段可能解决了问题,但是包括解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您的代码建议的原因。 - Gerhard
这是最好的答案。 - ScottyBlades

2

您可以直接使用UIImage上的isEqual方法比较两个图像是否完全相同。

因此,在您的测试中,您可以执行以下操作:

let expectedImage = UIImage(named: "my_picture")
let returnedImage = SomeImageReturnedFromFunction()
XCTAssertEqualObjects(expectedImage, returnedImage) // will return true if SomeImageReturnedFromFunction returns my_picture

参考资料: https://developer.apple.com/documentation/uikit/uiimage#overview

该文档介绍了如何使用UIImage类在iOS应用程序中处理图像。UIImage是一个对象,它表示一个图像,可以从文件、网络或其他来源加载图像。您可以使用UIImage对象来显示图像、创建缩略图、裁剪图像以及将图像保存到设备上的存储空间中。


0
你确实可以使用“isEqual”方法进行比较,但更好的方式是这样的:
let expectedImage = UIImage(named: "an_image")
let returnedImage = myFunctionRet() // optional ?

XCTAssert(returnedImage?.isEqual(expectedImage) == true)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接