无法在 XCUITest 中加载 Main.Storyboard

Could not load Main.Storyboard in XCUITest

我正在尝试为 UIViewcontrollers 制作 UITestCase,但是当我在我的 QuizAppUITests 中加载主情节提要时,它无法从 Bundle 中识别并且给出以下错误

Could not find a storyboard named 'Main' in bundle NSBundle </Users/mac/Library/Developer/CoreSimulator/Devices/CC199C69-F398-4A7C-882E-BFD3E72B95D3/data/Containers/Bundle/Application/BCC3F88D-E08C-4491-8340-699EAF98AB28/QuizAppUITests-Runner.app> (loaded) (NSInvalidArgumentException)

我也在 QuizAppUITests Target 中添加了 Main Storyboard 下面是我的测试代码

import XCTest
@testable import QuizApp

class QuizViewControllerUITests: XCTestCase {
    func makeSUT() -> QuizViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: type(of: self)))
        let sut = storyboard.instantiateViewController(withIdentifier: "QuizViewController") as! QuizViewController
        _ = sut.view
        return sut
    }
    
    func test_loadQuizViewController() {
        let sut = makeSUT()
        sut.headerQuestion = "Q1"
        XCTAssertEqual(sut.headerQuestion, "Q1")
    }

}

是否需要更改 QuizAppUITests Target 的 BuildSetting?

None 这对于 UI 测试是有意义的。相反,将您的代码放入单元测试目标,即 QuizAppTests.

我的书iOS Unit Testing by Example有一章叫做“加载视图控制器”。在那里寻找关于基于故事板的视图控制器的内容:

  • 不要在测试目标中包含故事板。仅将其放在您的应用目标中。
  • 然后你可以用UIStoryboard(name: "Main", bundle: nil)
  • 加载故事板
  • 有一种无需强制转换即可实例化视图控制器的新方法。使用 instantiateViewController(identifier:) 而不是 instantiateViewController(withIdentifier:) 并将其分配给显式类型的变量。

像这样:

let sut: QuizViewController = storyboard.instantiateViewController(
    identifier: "QuizViewController"
)

因为你使用class的名字作为标识符,我们甚至可以去掉字符串。这可以防止我们在编译时出现拼写错误:

let sut: QuizViewController = storyboard.instantiateViewController(
    identifier: String(describing: QuizViewController.self)
)

最后,我们可以更明确地加载视图,而不是 _ = sut.view

sut.loadViewIfNeeded()

这连接了插座连接。

同样,所有这些都属于您的单元测试目标 QuizAppTests,而不属于您的 UI 测试目标 QuizAppUITests

对于您的实际测试,不要只是在您的被测系统中分配一个 属性 并检查它是否已分配。那证明不了什么。相反,我会专注于测试:

  • 网点不为零
  • 与控件的交互如您所愿
  • 导航有效
  • 该视图外观与批准的快照相比没有变化

这一切都可以用 TDD 来完成。