我知道
@testable import MyModule
能够从"测试"中探索MyModule
的非公开成员。 (使用" testTarget"构建)模块MyModuleTests
。
我需要在我的"非测试"中使用相同的功能。模块。不在生产中,只是在调试模式下。
我的问题是:你知道怎么做吗?
相关(我认为,更难的问题):@testable
背后实际发生了什么魔法?
答案 0 :(得分:16)
要回答您的问题,出于调试目的,您实际上可以使用它。我们假设您有一个工作区MyAwesomeWkspace
和一个MyAwesomeProject
内的项目。
现在,创建一个名为framework
的新module
又名MyAwesomeModule
。在该模块内部创建一个名为Person
的非公共类。
如果您通过Person
尝试使用MyAwesomeProject
内的课程import MyAwesomeModule
,然后使用let 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)的一部分,防止了 外部实体访问在app中声明为内部的任何内容 或框架。默认情况下,为了能够从您的访问这些项目 测试代码,您需要至少提升他们的访问级别 公众,减少了Swift类型安全的好处。
Xcode为这个问题提供了一个由两部分组成的解决方案:
将“启用可测试性”构建设置设置为“是”时,即 对于新项目中的测试构建,默认情况下为true,Xcode包含 编译期间的-enable-testing标志。这使得在已编译模块中声明的Swift实体有资格获得更高级别的访问权限。 将@testable属性添加到导入语句时 在启用测试的情况下编译的模块,您激活提升的访问 对于该范围内的该模块。标记为的类和类成员 内部或公共行为就好像被标记为开放一样。其他实体 标记为内部行为,好像他们被宣布为公开。
更多here
延迟编辑:swift的一个很酷的部分是开源。因此,如果您想深入了解"魔法",请查看:https://github.com/apple/swift
答案 1 :(得分:-1)
@testable 导入
[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)
和 class 的 public
访问级别对于当前模块是 可见 为 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
}
}