问题描述
我正在尝试使用storyboard在swift中测试 UITableViewController
的子类。我能够获得对视图控制器的引用并将其记录下来,但我无法将其类型转换为我要测试的类,因此我无法访问方法,属性等。
I am trying to test an subclass of UITableViewController
in swift with storyboard. I am able to get a reference to the view controller and log it, but I am unable to typecast it to the class I am trying to test, so I can't access methods, properties etc.
获取我班级实例的正确方法是什么?
What's the proper way of getting a hold of the instance of my class?
import XCTest
import UIKit
class GameListControllerTest: XCTestCase {
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var _sut: AnyObject?
var sut: AnyObject {
if(_sut?) {
return _sut!
}
_sut = storyboard.instantiateViewControllerWithIdentifier("GameListController")
return _sut!
}
override func setUp() {
super.setUp()
println()
println("=================================")
println("sut \(sut)")
println("view \(sut.view)")
println("=================================")
}
func testInstance() {
var vc1 = sut as UITableViewController
var vc2 = sut as? GameListController
println()
println("=================================")
println("VC1 \(vc1)")
println("VC2 \(vc2)")
println("=================================")
}
}
输出:
Output:
XCTestOutputBarrierTest Suite '_TtC15mytabletopTests22GameListControllerTest' started at 2014-06-16 06:13:30 +0000
XCTestOutputBarrierTest Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]' started.
XCTestOutputBarrier
=================================
sut <_TtC10mytabletop18GameListController: 0xb338d50>
view <UITableView: 0xc033800; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0xb344960>; layer = <CALayer: 0xb33c7d0>; contentOffset: {0, 0}; contentSize: {480, 0}>
=================================
=================================
VC1 <_TtC10mytabletop18GameListController: 0xb338d50>
VC2 nil
=================================
Test Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]' passed (0.007 seconds).
XCTestOutputBarrierTest Suite '_TtC15mytabletopTests22GameListControllerTest' passed at 2014-06-16 06:13:30 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.007 (0.010) seconds
如果我试图强制类型转换( sut作为GameListController
),我会得到一个运行时异常。
If I try to force typecast (sut as GameListController
) I get an runtime exception.
推荐答案
这是发生了什么。我花了几天时间才弄明白,但我的问题在于如何将我的类导出到目标:
Here is what's happening. It took me days to figure out, but my problem was on how my classes were being exported to the targets:
这导致我的班级的两个二进制副本,一个在app目标和测试目标中的另一个。如果我们更加注意日志,我们可能会注意到:
This was causing two binary copies of my class, one in the app target and another in the test target. If we pay more attention to the logs we might notice:
Test Case '-[_TtC15mytabletopTests22GameListControllerTest testInstance]'
以上是测试方法 testInstance
,它是<$的一部分c $ c> mytabletopTests 执行上下文。现在让我们看看从Storyboard中提取的实例:
The above is the test method testInstance
and it's part of mytabletopTests
execution context. Now lets take a look at the instance pulled from Storyboard:
sut <_TtC10mytabletop18GameListController: 0xb338d50>
这反过来又在 mytabletop
上运行上下文。这解释了为什么测试无法转换为 GameListController
。它知道的 GameListController
是在测试目标内编译的。
This in turn is running on the mytabletop
context. This explains why the tests are unable to typecast to GameListController
. The GameListController
that it knows of is the one compiled inside the test target.
从测试中删除类target使我的测试用例不知道类,现在我需要将我的app目标导入测试用例:
Since removing the class from the test target makes the class unknown to my test case, now I need to import my app target into the test case:
import XCTest
import UIKit
import mytabletop // LINE ADDED
class GameListControllerTest: XCTestCase {
现在,测试可以访问的唯一 GameListController
与故事板实例化的相同,我终于能够输入它了。这是新的测试用例:
Now, the only GameListController
that the test have access to is the same one the storyboard is instantiating, and I am finally able to type cast it. Here is the new testcase:
import XCTest
import UIKit
import mytabletop
class GameListControllerTest: XCTestCase {
let sut: GameListController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("GameListController") as GameListController
override func setUp() {
super.setUp()
UIApplication.sharedApplication().keyWindow.rootViewController = sut
XCTAssertNotNil(sut.view)
}
func testInstance() {
XCTAssertNotNil(sut)
XCTAssertNotNil(sut.tableView) // UITableViewController property
XCTAssertNotNil(sut.store) // instance property
XCTAssertNotNil(sut.someButton) // outlet
}
}
现在我能够正确地进行类型转换(滚动)在实例初始化期间查看作为GameListController
)。为了强制所有出口正确绑定并且子视图相应地呈现正在运行测试的设备,我们可以使视图控制器为应用程序的 rootViewController
并拉动它的视图,如上面的 setUp
函数所示。即使 myCustomOutlet
现在也能正常工作。
Now I am able to typecast correctly (scroll to see as GameListController
) during the instance initialization. In order to force all outlets to be bound correctly and subviews to render accordingly to the device the test is being run, we can make the view controller the rootViewController
for the app and pull the view from it, as seen in the setUp
function above. Even myCustomOutlet
is now working correctly.
这篇关于如何在Swift中对一个子类UIViewController进行类型转换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!