如何对在 Swift 中调用的私有方法进行单元测试
Posted
技术标签:
【中文标题】如何对在 Swift 中调用的私有方法进行单元测试【英文标题】:How to unit test that private method is called in Swift 【发布时间】:2019-01-08 03:13:53 【问题描述】:我有一个 ViewController 类,它呈现一系列的两个选择弹出视图。每两个选项的弹出视图都是不同的。
Popup1 - Choice1 -> Choice1Popup
Popup1 - Choice2 -> Choice2Popup
我希望呈现 Popup1 的方法是公开的,但我希望呈现 Choice1Popup 和 Choice2Popup 的其他方法是私有的。
如果我决定需要测试 Choice1Popup 和 Choice2Popup,那么我可能必须将它们设为内部而不是私有,但它们不太可能在任何其他地方使用。
我想编写一个单元测试来测试当 Choice1 的按钮被触摸时是否调用了呈现 Choice1Popup 的方法。我使用了带有方法类型变量的协议,以允许 Mock 注入弹出演示者的 Mock 版本。我对自己的方法不是 100% 满意,所以我想了解是否有更好的方法。
除此之外,我对内部与私人之间感到矛盾。能够测试我的私有方法会很好,但我不希望它们能够从任何地方被调用,但单元测试并使它们内部暴露它们。
这是代码,底部有一个单元测试:
// 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")
【问题讨论】:
***.com/a/61348578/6799777 【参考方案1】:测试无法访问声明为 private
的任何内容。他们可以访问声明为internal
的任何内容,只要测试代码执行@testable import
。
当你感到“但我不应该公开这个”时,你会觉得你的类实际上有多个接口。有“它所做的一切接口”和“生产代码接口所需的部分”。有很多事情需要考虑:
是否还有其他类型的人正在尝试脱身? 是否有其他协议来表达接口的子集?其余的生产代码都可以使用它。 或者,它可能就像一个家庭影院放大器,其中“您不需要经常使用的控件”隐藏在面板后面。不要出汗。【讨论】:
是的,我知道我可以将方法设为内部而不是私有,并且可以在我的单元测试中访问它们。在这种情况下,会发生一系列两个选择弹出窗口。现在只有第一个是公开的,其余的是私有的。但是,如果我不必将它们设为内部,那么对它们进行测试也会很好。我也想做一个端到端的测试,但除非我把它们放在内部,否则必须用 UI 测试来做。因此,根据模拟类的公共测试技术。这一切对你好吗?没有更好的方法?这对我来说似乎是最好的。 什么调用了你的私有方法?有没有机会通过它而不是私有方法调用测试? Choice1 和 Choice2 方法类型变量在 UserChoices 类中分配了私有方法。这些私有方法只能从公共 presentChoiceViewController() 调用。他们的模拟版本被注入到单元测试中。这些 UserChoice 类私有方法依次打开另外 2 个选择弹出窗口,这些弹出窗口使用私有结构,用于保存一些收集的信息。但是除了单元测试之外,没有理由通过将它们设置为内部来公开它们。 我敢打赌还有另一种引入测试接缝的方法。是什么阻止您在没有UserChoicesPrivateUnitTesting
的情况下使用更直接的代码?也就是说,如果您编写代码而不注入方法而只是调用您想要的,您会发现难以拦截的内容是什么?是present
电话吗?
问题是我想让choice1 和choice2 方法私有而不是内部的,而且由于choice1 和choice2 的真实版本(与模拟版本相反)会创建更多的两个选择弹出窗口,所以我不这样做不想在单元测试中调用它们。 IE。我只想测试对于前两个选择弹出窗口,每个按钮操作都会调用具有正确签名的正确方法。我希望我的解释足够清楚。以上是关于如何对在 Swift 中调用的私有方法进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章