我有一个 ViewController 类,它提供了一系列有两种选择的弹出 View 。每两个选择弹出 View 都是不同的。
Popup1 - Choice1 -> Choice1Popup
Popup1 - Choice2 -> Choice2Popup
我希望呈现 Popup1 的方法是公开的,但我希望呈现 Choice1Popup 和 Choice2Popup 的其他方法是私有(private)的。
如果我决定我需要测试 Choice1Popup 和 Choice2Popup,那么我可能必须将它们设为内部而不是私有(private),但它们不太可能在任何其他地方使用。
我想编写一个单元测试来测试在触摸 Choice1 的按钮时调用显示 Choice1Popup 的方法。我使用了一个带有方法类型变量的协议(protocol)来允许模拟注入(inject)弹出演示者的模拟版本。我对我的方法不是 100% 满意,所以我想就是否有更好的方法征求意见。
顺便说一句,我对内部和私有(private)感到矛盾。能够测试我的私有(private)方法会很好,但我不希望它们能够从除单元测试之外的任何地方调用并使它们在内部公开。
这是代码,底部是一个单元测试:
// protocol to be used by both UserChoices class and UserChoicesMock for method injection
protocol UserChoicesPrivateUnitTesting {
static var choice1Method:(UIViewController) -> Void { get set }
static var choice2Method:(UIViewController) -> Void { get set }
}
// this popup that will be presented with a public method
public class ChoiceViewController:UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subjectLabel: UILabel!
@IBOutlet weak var choice1Button: UIButton!
@IBOutlet weak var choice2Button: UIButton!
var choice1Action:(() -> Void)?
var choice2Action:(() -> Void)?
// ...
}
public class UserChoices: UIViewController, UserChoicesPrivateUnitTesting {
static var choice1Method: (UIViewController) -> Void = choice1
static var choice2Method: (UIViewController) -> Void = choice2
private static func choice1(onTopViewController: UIViewController) {
//present choice1Popup
}
private static func choice2(onTopViewController: UIViewController) {
//present choice2Popup
}
public static func presentChoiceViewController(onTopViewController: UIViewController, ChoiceViewController: ChoiceViewController = ChoiceViewController.instantiateFromAppStoryBoard(appStoryBoard: .MenuStoryboard)) {
let isCustomAnimated = true
// ChoiceViewController.transitioningDelegate = transitionDelegate
ChoiceViewController.choice1Action = { [weak onTopViewController]() in
guard let weakSelf = onTopViewController else {
return
}
weakSelf.dismiss(animated: false, completion: nil)
UserChoices.choice1Method(onTopViewController!)
}
ChoiceViewController.choice2Action = { [weak onTopViewController]() in
guard let weakSelf = onTopViewController else {
return
}
weakSelf.dismiss(animated: false, completion: nil)
UserChoices.choice2Method(onTopViewController!)
}
onTopViewController.present(ChoiceViewController, animated: isCustomAnimated, completion: nil)
}
}
import XCTest
@testable import ChoiceModule
public class UserChoicesMock:UserChoicesPrivateUnitTesting {
static public var choice1Method: (UIViewController) -> Void = choice1
static public var choice2Method: (UIViewController) -> Void = choice2
static var choice1MethodCalled = false
static var choice2MethodCalled = false
static func choice1(onTopViewController: UIViewController) {
choice1MethodCalled = true
}
static func choice2(onTopViewController: UIViewController) {
choice2MethodCalled = true
}
}
class UserChoicesTests: XCTestCase {
func testChoice1CallsPrivateChoice1Method() {
// This is an example of a functional test case.
let vc = UIViewController()
let choiceViewController = ChoiceViewController.instantiateFromAppStoryBoard(appStoryBoard: .MenuStoryboard)
UserChoices.choice1Method = UserChoicesMock.choice1Method
UserChoices.presentChoiceViewController(onTopViewController: vc, ChoiceViewController: choiceViewController)
choiceViewController.choice1Button.sendActions(for: .touchUpInside)
if UserChoicesMock.choice1MethodCalled == false {
XCTFail("choice1Method not called")
}
}
}
最佳答案
测试无法访问任何声明为 private
的内容。只要测试代码执行 @testable import
,他们就可以访问声明为 internal
的任何内容。
当您有一种反胃的感觉时,“但我不应该公开这个”,请考虑您的类实际上有多个接口(interface)。有“它所做的一切接口(interface)”和“生产代码接口(interface)所需的部分”。有很多事情需要考虑:
- 是否有另一种类型试图逃脱?
- 是否有另一种协议(protocol)来表达接口(interface)的子集?这可以被其余的生产代码使用。
- 或者它可能就像一个家庭影院放大器,其中“您并不经常需要的控件”隐藏在面板后面。不要担心。
关于swift - 如何对在 Swift 中调用的私有(private)方法进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54084767/