测试与调试 - Swift SDK
在此页面上
测试
使用默认 Realm 进行测试
最简单使用和测试 Realm 支持应用程序的方法是使用默认 Realm。为避免在测试之间覆盖应用程序数据或泄漏状态,请将默认 Realm 设置为每个测试的新文件。
// A base class which each of your Realm-using tests should inherit from rather // than directly from XCTestCase class TestCaseBase: XCTestCase { override func setUp() { super.setUp() // Use an in-memory Realm identified by the name of the current test. // This ensures that each test can't accidentally access or modify the data // from other tests or the application itself, and because they're in-memory, // there's nothing that needs to be cleaned up. Realm.Configuration.defaultConfiguration.inMemoryIdentifier = self.name } }
注入 Realm 实例
另一种测试 Realm 相关代码的方法是让所有要测试的方法接受 Realm 实例作为参数。这样,在运行和测试应用程序时,您就可以传递不同的 Realm。
例如,假设您的应用程序具有通过 JSON API GET
用户配置文件的方法。您想要测试是否正确创建了本地配置文件:
// Application Code func updateUserFromServer() { let url = URL(string: "http://myapi.example.com/user") URLSession.shared.dataTask(with: url!) { data, _, _ in let realm = try! Realm() createOrUpdateUser(in: realm, with: data!) } } public func createOrUpdateUser(in realm: Realm, with data: Data) { let object = try! JSONSerialization.jsonObject(with: data) as? [String: String] try! realm.write { realm.create(User.self, value: object, update: .modified) } } // Test Code let realmPath = URL(fileURLWithPath: "...") func testThatUserIsUpdatedFromServer() { let config = Realm.Configuration(fileURL: realmPath) let testRealm = try! Realm(configuration: config) let jsonData = "{\"email\": \"help@realm.io\"}".data(using: .utf8)! // In our test, we're passing in the testRealm. This is where we'd // pass in our "real" realm in the application code above. createOrUpdateUser(in: testRealm, with: jsonData) XCTAssertEqual(testRealm.objects(User.self).first!.email, "help@realm.io", "User was not properly updated from server.") }
利用类预测简化测试
10.21.0 版新增功能。
如果要使用对象属性的子集进行测试,可以创建类投影。类投影是一种模型抽象,您可以在其中传递、重命名或排除 Realm 对象属性。虽然此功能简化了视图模型实现,但它也简化了使用 Realm 的测试。
例子
本示例使用了定义和使用类投影页面的对象模型和类投影。
在这个示例中,我们使用完整对象模型创建一个Realm 对象。然后,我们将该对象视为类投影,仅使用其属性的子集进行操作。
借助此类投影,我们就不需要访问或考虑那些不需要测试的属性。
func testWithProjection() { let realm = try! Realm() // Create a Realm object, populate it with values let jasonBourne = Person(value: ["firstName": "Jason", "lastName": "Bourne", "address": [ "city": "Zurich", "country": "Switzerland"]]) try! realm.write { realm.add(jasonBourne) } // Retrieve all class projections of the given type `PersonProjection` // and filter for the first class projection where the `firstName` property // value is "Jason" let person = realm.objects(PersonProjection.self).first(where: { $0.firstName == "Jason" })! // Verify that we have the correct PersonProjection XCTAssert(person.firstName == "Jason") // See that `homeCity` exists as a projection property // Although it is not on the object model XCTAssert(person.homeCity == "Zurich") // Change a value on the class projection try! realm.write { person.firstName = "David" } // Verify that the projected property's value has changed XCTAssert(person.firstName == "David") }
测试目标
请勿将 Realm 框架直接链接到测试目标。这可能导致测试失败,并出现“对象类型 'YourObject' 不受 Realm 管理”的异常消息。取消 Realm 与测试目标的链接,应该可以解决此问题。
在应用程序或框架目标中编译模型类文件;不要将它们添加到单元测试目标中。否则,这些类在测试时会重复,可能导致难以调试的问题。
向单元测试目标公开测试所需的所有代码。使用public
访问权限修饰符或 @testable。
因为您将 Realm 作为动态框架,所以需要确保单元测试目标能找到 Realm。将 RealmSwift.framework
的父路径添加到单元测试的“框架搜索路径”。
调试
使用 Realm Studio 调试
Realm Studio 使您能够打开和编辑本地 Realm。它支持 Mac、Windows 和 Linux。
LLDB
使用 Realm 的 Swift API 调试应用程序必须通过 LLDB 控制台完成。
虽然 LLDB 脚本允许在 Xcode 的 UI 中检查 Realm 变量的内容,但这不适用于 Swift。这些变量将显示不正确的数据。相反,请使用 LLDB 的 po
命令来检查 Realm 中存储的数据内容。
故障排除
解决构建问题
一些开发者在通过 CocoaPods 或 Carthage 安装 Realm Swift SDK 后遇到构建问题。这些问题的常见原因包括:
安装问题:
初始安装失败
使用不支持的依赖项管理器版本
构建工具问题:
构建工具有过时的缓存
更新构建工具版本
变更项目设置,例如:
添加新目标
跨目标共享依赖项
这些问题的修复方法通常是删除派生数据并清理 Xcode 构建文件夹。
加载 UI 前打开 Realm 的问题
您可以打开一个 Realm,并立即看到崩溃以及与可选或必需属性相关的错误消息。对象模型的问题可能导致此类崩溃。这些错误发生在您打开 Realm 之后但在进入 UI 之前。
在设备上打开 Realm 时,Realm 有一个“模式发现”阶段。此时,Realm 检查其管理的任何对象的模式。您可以指定给定 Realm 应仅管理应用程序中对象的子集。
如果您在模式发现期间看到与属性相关的错误,这些错误可能是由于模式问题而不是来自特定对象的数据问题造成的。例如,如果您将对一关系定义为必填关系而不是可选关系,则可能会出现模式发现错误。
要调试这些崩溃,请检查您定义的模式。
您可以看出这些是模式发现问题,因为它们发生在用户界面加载之前。这意味着没有用户界面元素尝试错误地使用属性,内存中不也存在任何可能包含错误数据的对象。如果您在用户界面加载后收到与属性相关的错误,这可能不是由于无效模式造成的。这些可能是由于数据不正确、输入错误或缺失造成的。
没有为模型定义属性
Realm Swift SDK 使用 Swift 语言的反射功能在运行时确定模型中的属性。如果您遇到类似于以下的崩溃,请确认项目没有禁用反射元数据:
Terminating app due to uncaught exception 'RLMException', reason: 'No properties are defined for 'ObjectName'.
如果设置 SWIFT_REFLECTION_METADATA_LEVEL = none
,Realm 将无法发现属性和枚举等类型的子类型。如果您的项目未专门为此设置设定级别,则默认情况下会启用反射。
分配错误/可用内存不足
在可用内存很少的 iOS 或 iPad 设备中,或者使用多个 Realm 或许多通知的内存密集型应用程序中,您可能会遇到以下错误:
libc++abi: terminating due to an uncaught exception of type std::bad_alloc: std::bad_alloc
该错误通常表示由于可用内存不足而无法分配资源。
如果您正在针对 iOS15 + 或 iPad15 + 进行构建,则可以添加 扩展虚拟寻址权利 以解决此问题。
将这些键添加到 Property List 中,并将值设置为 true
:
<key>com.apple.developer.kernel.extended-virtual-addressing</key> <true/> <key>com.apple.developer.kernel.increased-memory-limit</key> <true/>
无法动态构建 Swift 包目标
在版本10.49.3中进行了更改。
Swift SDK v 10.49.3 更改了使用 Swift Package Manager (SPM) 安装软件包的详细信息。 当您从旧版本的软件包更新到 v 10.49.3或更高版本时,可能会收到类似以下内容的构建错误:
Swift package target `Realm` is linked as a static library by `TargetName` and `Realm`, but cannot be built dynamically because there is a package product with the same name.
要解决此错误,请取消Realm
或RealmSwift
包与构建目标的链接。 您可以按照以下步骤在 Xcode 中执行此操作:
在项目 Targets中,选择构建目标。
转到 Build Phases 标签页。
展开Link Binary With Libraries元素。
选择
Realm
或RealmSwift
,然后单击“删除项目”( - ) 按钮,删除不需要的二进制文件。如果您使用 Swift 或 Swift 和Objective-C API,请保留
RealmSwift
。如果仅使用Objective-C API,请保留
Realm
。
现在,您的目标在构建时应该不会出现此错误。