【问题标题】:What's happening behind the scenes in XCTest's @testable?XCTest 的@testable 幕后发生了什么?
【发布时间】:2018-03-29 14:55:55
【问题描述】:

我知道

@testable import MyModule

能够从“测试”(使用“testTarget”构建)模块MyModuleTests 探索MyModule 的非公共成员。

我的“非测试”模块需要相同的功能。不在生产中,只是处于调试模式。

我的问题是:你知道怎么做吗?

相关(我认为,更难的问题):@testable 背后究竟发生了什么魔法?

【问题讨论】:

  • @testable 背后到底发生了什么魔法 好像编译器只是不对非公共成员实施访问限制,不是吗?
  • 我认为能够回答幕后情况的只有 Apple 员工。

标签: ios swift macos xctest


【解决方案1】:

要回答您的问题,出于调试目的,您实际上可以使用它。假设您有一个工作区MyAwesomeWkspace 和一个位于MyAwesomeProject 内的项目。

现在,创建一个名为 MyAwesomeModule 的新 framework 又名 module。在该模块中创建一个名为Person 的非公共类。

如果你尝试在MyAwesomeProject 中使用Person 类,通过执行import MyAwesomeModulelet p = Person() 之类的操作,将会出错。

但是,如果您使用@testable import MyAwesomeModule,那么神奇的事情就会发生,您现在可以使用该类了。

基本上@testable 允许你测试你没有公开的东西。该注释仅适用于import,正如您所见here

所以为了工作,目标是用-enable-testing编译的,这样你就可以访问非公共成员了。至少基于here是什么

因为默认情况下,debug 构建配置是使用-enable-testing 编译的,所以我向您展示的示例将起作用。但是,如果您将构建配置更改为release,您将看到一条错误消息Module .. was not compiled for testing,因为release 配置不是使用该标志构建的。

Swift 访问控制模型,如访问控制中所述 Swift 编程语言 (Swift 4) 的部分,防止 外部实体访问应用程序中声明为内部的任何内容 或框架。默认情况下,为了能够从您的 测试代码,您需要将其访问级别至少提升到 public,减少了 Swift 类型安全的好处。

Xcode 为这个问题提供了一个两部分的解决方案:

当您将“启用可测试性”构建设置设置为“是”时,即 新项目中的测试版本默认为 true,Xcode 包括 -enable-testing 编译期间的标志。这使得编译模块中声明的 Swift 实体有资格获得更高级别的访问。 当您将 @testable 属性添加到导入语句时 启用测试编译的模块,您激活提升的访问权限 对于该范围内的该模块。类和类成员标记为 internal 或 public 的行为就像它们被标记为打开一样。其他实体 标记为内部行为,就好像它们被宣布为公共一样。

更多here

后期编辑:Swift 最酷的部分之一是开源。所以如果你想深入了解“魔法”,请查看:https://github.com/apple/swift

【讨论】:

    【解决方案2】:

    @testable 导入 和 -enable-testing

    [Swift access modifiers]
    [Swift module]

    consumer side uses @testable import -> producer side should use `-enable-testing` flag
    

    生产者端:启用-enable-testing

    • Enable Testability(ENABLE_TESTABILITY) - 是的
    • Other Swift Flags(OTHER_SWIFT_FLAGS) - -enable-testing

    消费者方面:@testable

    • internal(default)publicclass 访问级别对于当前模块为 visible open

    • internal(default) 其他人的访问级别(结构,枚举)对于当前模块为 可见 public

    如果您使用 @testable 构建测试架构(消费者),但生产者不包含 -enable-testing,您会得到

    Module '<module_name>' was not compiled for testing
    

    一些实验:

    一些模块

    internal class SomeInternalClass {
        internal func foo() { }
    }
    
    public class SomePublicClass {
        public func foo() { }
    }
    
    internal class SomeInternalStruct {
        internal func foo() { }
    }
    
    internal enum SomeInternalEnum: String {
        case foo = "hello world"
    }
    

    测试:如果您省略@testable,将出现下一个错误

    import XCTest
    
    @testable import ExperimentsTests
    
    class ExperimentsTestsTests: XCTestCase {
    
        func testExample() throws {
            
            let someInternalStruct = SomeInternalStruct() //Cannot find 'SomeInternalStruct' in scope
            someInternalStruct.foo()
            
            let someInternalEnum = SomeInternalEnum(rawValue: "") //Cannot find 'SomeInternalEnum' in scope
            SomeInternalEnum.foo //Cannot find 'SomeInternalEnum' in scope
        }
        
        class SomePublicSubClass: SomePublicClass { //Cannot inherit from non-open class 'SomePublicClass' outside of its defining module
            override func foo() { } //Overriding non-open instance method outside of its defining module
        }
        
        class SomeInternalSubClass: SomeInternalClass { //Cannot find type 'SomeInternalClass' in scope
            override func foo() { } //Method does not override any method from its superclass
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-19
      • 1970-01-01
      • 2011-07-12
      • 1970-01-01
      • 2019-06-05
      • 1970-01-01
      • 2010-09-06
      • 2018-12-05
      • 2016-02-23
      相关资源
      最近更新 更多