在 Realm 中测试相等性

Posted

技术标签:

【中文标题】在 Realm 中测试相等性【英文标题】:Testing for equality in Realm 【发布时间】:2016-08-10 09:00:23 【问题描述】:

我正在尝试在单元测试中测试 Realm 对象之间的相等性。但是,我无法让对象返回 true 以保证它们的相等性。

根据Realm docs here,我应该可以做到:

let expectedUser = User()
expectedUser.email = "help@realm.io"
XCTAssertEqual(testRealm.objects(User.self).first!,
               expectedUser,
               "User was not properly updated from server.")

但是,我使用以下代码得到以下测试失败:

领域模型

class Blurb: Object 
    dynamic var text = ""

测试

func testRealmEquality() 
    let a = Blurb()
    a.text = "asdf"
    let b = Blurb()
    b.text = "asdf"
    XCTAssertEqual(a, b)

XCTAssertEqual 失败:(“可选(Blurb 文本 = asdf; )") 不等于 ("Optional(Blurb 文本 = asdf; )")

【问题讨论】:

【参考方案1】:

来自 Realm 的 Katsumi。 Realm 对象的Equatable 实现如下:

BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) 
    // if not the correct types throw
    if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) 
        @throw RLMException(@"Can only compare objects of class RLMObjectBase");
    
    // if identical object (or both are nil)
    if (o1 == o2) 
        return YES;
    
    // if one is nil
    if (o1 == nil || o2 == nil) 
        return NO;
    
    // if not in realm or differing realms
    if (o1->_realm == nil || o1->_realm != o2->_realm) 
        return NO;
    
    // if either are detached
    if (!o1->_row.is_attached() || !o2->_row.is_attached()) 
        return NO;
    
    // if table and index are the same
    return o1->_row.get_table() == o2->_row.get_table()
        && o1->_row.get_index() == o2->_row.get_index();

总而言之,a) 如果两个对象都是非托管的,它的工作方式与普通对象的Equatable 相同。 b) 如果两个对象都是托管的,如果它们是相同的表(类)和索引,它们是相等的。 c) 如果一个是托管的,另一个是非托管的,那么它们就不相等。

“托管”表示对象已存储在 Realm 中。

所以你的代码中的ab 是不相等的。因为ab 是非托管的(没有存储在Realm 中)并且它们是不同的对象。

let a = Blurb()
a.text = "asdf"
let b = Blurb()
b.text = "asdf"
XCTAssertEqual(a.text, b.text)

此外,在测试相等性时,Realm 并不关心对象的值。 Realm 只检查表和行索引(如“b)”所述)。因为不同的对象存在相同的值在数据库中是正常的。

两个对象相等的例子如下:

let a = Blurb()
a.text = "asdf"

let realm = try! Realm()
try! realm.write 
    realm.add(a)


let b = realm.objects(Blurb.self).first!
print(a == b) // true

【讨论】:

感谢您的解释。但是,在领域文档代码示例中,expectedUser 不是一个仅具有相同电子邮件属性值的非托管对象吗?为什么该测试会通过? @solidcell 很抱歉造成混乱。我认为该文件不正确或为简单起见省略了某些内容。我会尽快修复文档的问题,谢谢指出! 这种行为改变了吗?看起来至少从 Realm 3.7.4 开始,Equatable 仅适用于具有主键的对象;当我在没有主键的类上尝试与上述类似的创建/添加/检索循环时,我得到 isSameObject(as:) 返回 true,但 isEqual() 返回 false。【参考方案2】:

您知道比较协议在 ios 上的工作原理吗?

如果没有,请查看此处 http://nshipster.com/swift-comparison-protocols/

基本上如果你这样做

let a = NSObject()
let b = NSObject()
let c = a
a == b // false
a == c // true

你应该创建一个这样的类符合 Equatable

class MyClass: Equatable 
  let myProperty: String

  init(s: String) 
    myProperty = s
  


func ==(lhs: MyClass, rhs: MyClass) -> Bool 
  return lhs.myProperty == rhs.myProperty


let myClass1 = MyClass(s: "Hello")
let myClass2 = MyClass(s: "Hello")
myClass1 == myClass2 // true
myClass1 != myClass2 // false
myClass1 === myClass2 // false
myClass1 !== myClass2 // true

我建议检查 .text 进行测试

func testRealmEquality() 
    let a = Blurb()
    a.text = "asdf"
    let b = Blurb()
    b.text = "asdf"
    XCTAssertEqual(a.text, b.text)

【讨论】:

是的,我知道Equatable 是如何工作的,但是我不确定Realm 是如何实现它的,也不知道为什么他们的文档显示它显然能够用于测试具有可比性的对象(但是不是同一个确切的对象)。如果可能的话,我宁愿不必为数组中的每个对象测试每个属性。这往往会失控。 最后的建议。您是否检查过有关此的 Realm 文档? realm.io/docs/swift/latest/#testing 是的,看看我的问题的开头,你会看到我已经链接到文档并粘贴了相关的 sn-p。 是的。对不起这个问题。我用 Realm 和 TDD 检查了我的项目,但我从不比较整个对象。

以上是关于在 Realm 中测试相等性的主要内容,如果未能解决你的问题,请参考以下文章

*通过共享* JavaScript 对象测试深度相等性

C ++中派生类的相等性测试[重复]

如何在 CoreBluetooth 中测试两个 CBPeripheral 的相等性?

预处理器相等性测试,这是标准吗?

在Java中测试两个JSON对象的相等性忽略子顺序[关闭]

测试 R 中多个系数的相等性