代码之家  ›  专栏  ›  技术社区  ›  bhartsb

如何在swift中对私有方法进行单元测试

  •  1
  • bhartsb  · 技术社区  · 6 年前

    我有一个viewcontroller类,它显示一系列两种选择的弹出视图。每两个选项弹出视图都是不同的。

    弹出1-选项1->选项1弹出

    弹出1-选项2-选项2弹出

    我打算将popup1呈现为公共的方法,但我希望呈现choice1popup和choice2popup的其他方法是私有的。

    如果我决定要测试选项1选项和选项2选项,那么我可能需要将它们设置为内部而不是私有,但它们不太可能在其他地方使用。

    我想编写一个单元测试,测试当触摸choice1的按钮时,是否调用表示choice1popup的方法。我使用了一个带有方法类型变量的协议来允许模拟注入弹出式展示器的模拟版本。我对自己的方法不太满意,所以我想知道是否有更好的方法。

    顺便说一下,我对内部和私人之间的冲突感到很矛盾。能够测试我的私有方法是很好的,但是我不希望它们能够从任何地方被调用,除了单元测试,并且使它们内部公开。

    下面是代码,下面是单个单元测试:

    // 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")
            }
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Jon Reid    6 年前

    测试无法访问声明的任何内容 private . 他们可以访问任何声明 internal 只要测试代码 @testable import .

    当你有那种不安的感觉,“但是我不应该公开这个”,考虑一下你的类实际上有多个接口。这里有“它所做的一切接口”,还有“生产代码接口所需的部分”。关于这一点,有很多事情需要考虑:

    • 有没有其他类型的人想出去?
    • 是否有其他协议来表示接口的子集?这可以被其余的生产代码使用。
    • 或者它就像家庭影院的扩音器,在面板后面隐藏着“你不需要的控制器”。别发汗。