Texture(AsyncDisplayKit)에서 UI 스냅샷 테스트를 하고 싶어졌다. 하지만 iOSSnapshotTestCase의 기본 예제로는 사용이 불가능하다.
혹시나 싶어 Texture 깃허브를 찾아보았더니 이미 iOSSnapshotTestCase를 사용하고 있었다.
https://github.com/TextureGroup/Texture/blob/master/Tests/ASSnapshotTestCase.mm
위는 Objective-C로 작성되어있어서 Swift 프로젝트에 적용하기에 번거롭다는 단점이 있어서 조금 포팅을 했다. (일부 함수들은 매크로로 정의되어 있는 것 같아서 함수로 따로 빼두었다)
import AsyncDisplayKit
import FBSnapshotTestCase
class ASSnapshotTestCase: FBSnapshotTestCase {
let helper = ASDisplayNodeTestsHelper()
func ASSnapshotTestCaseDefaultSuffixes() -> NSOrderedSet {
let suffixesSet: NSMutableOrderedSet = []
suffixesSet.add("_64")
return suffixesSet
}
/// Hack for testing. ASDisplayNode lacks an explicit -render method, so we manually hit its layout & display codepaths.
func hackilySynchronouslyRecursivelyRenderNode(_ node: ASDisplayNode) {
// Disable asynchronous display for rendering snapshots since things like UITraitCollection are thread-local
// so changes to them (`-[UITraitCollection performAsCurrentTraitCollection]`) aren't preserved across threads.
// Since the goal of this method is to just to ensure a node is rendered before snapshotting, this should be reasonable default for all callers.
#if AS_AT_LEAST_IOS13
if #available(iOS 13.0, *) {
ASTraitCollectionPropagateDown(node, ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection.current))
}
#endif
node.displaysAsynchronously = false
ASDisplayNodePerformBlockOnEveryNode(nil, node, true, { node in
node.layer.setNeedsDisplay()
})
node.recursivelyEnsureDisplaySynchronously(true)
}
func layoutSpecUnderTest(
_ layoutSpec: ASLayoutSpec,
sizeRange: ASSizeRange,
subnodes: [ASDisplayNode]
) {
let node = ASTestNode()
subnodes.forEach { node.addSubnode($0) }
node.layoutSpecUnderTest = layoutSpec
ASDisplayNodeSizeToRange(node: node, range: sizeRange)
ASSnapshotVerifyLayer(node: node)
}
func ASSnapshotVerifyNode(node: ASDisplayNode) {
hackilySynchronouslyRecursivelyRenderNode(node)
FBSnapshotVerifyView(node.view)
}
func ASSnapshotVerifyLayer(node: ASDisplayNode) {
hackilySynchronouslyRecursivelyRenderNode(node)
FBSnapshotVerifyLayer(node.layer)
}
func ASDisplayNodeSizeToFitSize(node: ASDisplayNode, size: CGSize) {
helper.ASDisplayNodeSizeToFitSize(node, size)
}
func ASDisplayNodeSizeToRange(node: ASDisplayNode, range: ASSizeRange) {
helper.ASDisplayNodeSizeToFitSizeRange(node, range)
}
}
사용은 아래처럼 하면 된다.
class IWCartEstimateDetailModifyHeaderNodeTests: ASSnapshotTestCase {
override func setUp() {
super.setUp()
recordMode = false
}
func testNode() {
let node = IWCartEstimateDetailModifyHeaderNode()
ASDisplayNodeSizeToFitSize(node: node, size: .init(width: 375, height: 50))
ASSnapshotVerifyNode(node: node)
}
}
끝!