视图测试关注点:

  • tableview的行数是否正确?
  • label的文本显示是否正确?
  • button 是否启用或禁用?
  • view的frame是否正确?

viewController如果可以从他们依赖项隔离出来,那么就可以测试。依赖注入是一种可以把视图控制器隔离出来的技术。在测试中,我们可以用假数据替换依赖项,模拟真实数据的行为。
viewController有两个职责:渲染数据、响应用户交互。在MVVM设计模式中,控制器不主动从model中拉数据,也不负责从model中取出数据来更新控制器。

我们定义好要显示的状态属性等,然后通过协议来约束控制器实现渲染数据的动作。

1
2
3
4
5
6
7
8
9
10
struct ArtistDetailProps {
let title: String
let fullName: String
let numberOfAlbums: String
let numberOfFollowers: String
}

protocol ArtistDetailComponent: AnyObject {
func render(_ props: ArtistDetailProps)
}

viewController遵循该协议,实现渲染数据的动作
1
2
3
4
5
6
7
8
extension ArtistDetailViewController: ArtistDetailComponent {
func render(_ props: ArtistDetailProps) {
navigationItem.title = props.title
fullNameLabel.text = props.fullName
numberOfAlbumsLabel.text = props.numberOfAlbums
numberOfFollowersLabel.text = props.numberOfFollowers
}
}

测试ViewController

控制器的render是由presenter决定,我我们可以定义一个presenter的mockPresenter子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义好交互方法
class ArtistDetailPresenterMock: ArtistDetailPresenter {

private(set) var onViewLoadedCalled = false

func onViewLoaded() {
onViewLoadedCalled = true
}

private(set) var onEditCalled = false

func onEdit() {
onEditCalled = true
}
}

准备好测试数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class ArtistDetailViewControllerTests: XCTestCase {
let presenter = ArtistDetailPresenterMock()

func makeSUT() -> ArtistDetailViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sut = storyboard.instantiateViewController(identifier: "ArtistDetailViewController") as! ArtistDetailViewController
sut.presenter = presenter
sut.loadViewIfNeeded()
return sut
}


//验证交互:
func testViewDidLoadCallsPresenter() {
let sut = makeSUT()

sut.viewDidLoad()

XCTAssertTrue(presenter.onViewLoadedCalled)
}

func testOnEditCallsPresenter() {
let sut = makeSUT()

sut.onEdit(.init())

XCTAssertTrue(presenter.onEditCalled)
}

//验证测试数据是否正确
func testRender() {
let props = ArtistDetailProps(title: "TITLE", fullName: "NAME", numberOfAlbums: "1", numberOfFollowers: "2")

let sut = makeSUT()

sut.render(props)

XCTAssertEqual(sut.navigationItem.title, "TITLE")
XCTAssertEqual(sut.fullNameLabel.text, "NAME")
XCTAssertEqual(sut.numberOfAlbumsLabel.text, "1")
XCTAssertEqual(sut.numberOfFollowersLabel.text, "2")
}
}

ok,以上测试用例我们验证了用户交互行为是否触发 以及 渲染数据的正确与否。
总结:如果要让viewController可测试,我们需要让viewController数据渲染被动而非主动(MVVM模式或者MVP模式),可以通过依赖注入来定义mock模型,实现可测试化。控制器和视图的测试不是测试颜色、布局等样式,而是测试交互以及数据渲染的准确性!

评论