solidcell
solidcell

Reputation: 7739

Testing for equality in Realm

I'm trying to test for equality among Realm objects in unit tests. However, I'm unable to get objects to return true for their equality.

According to the Realm docs here, I should be able to do that:

let expectedUser = User()
expectedUser.email = "[email protected]"
XCTAssertEqual(testRealm.objects(User.self).first!,
               expectedUser,
               "User was not properly updated from server.")

However, I get the following test failure with the following code:

Realm Model

class Blurb: Object {
    dynamic var text = ""
}

Test

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

XCTAssertEqual failed: ("Optional(Blurb {
text = asdf;
})") is not equal to ("Optional(Blurb {
text = asdf;
})")

Upvotes: 6

Views: 4328

Answers (2)

kishikawa katsumi
kishikawa katsumi

Reputation: 10573

Katsumi from Realm here. Realm object's Equatable is implemented as follows:

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();
}

In summary, a) if both objects are unmanaged, it works same as normal object's Equatable. b) if both objects are managed, if they are the same table (class) and index, they are equal. c) If one is managed, another is unmanaged, ther are not equal.

"managed" means the object has stored in Realm.

So a and b in your code is not equal. Because a and b are unmanaged (have not stored in Realm) and they are different objects.

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

Furthermore, when testing the equality, Realm doesn't care the values of the objects. Realm checks only a table and row index (as mentioned "b)"). Because different objects that has the same value are stored in the database is normal.

2023 Edit - the below is only true if the object has a primary key, so the Blurb object would need to be defined as

class Blurb: Object {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var text = ""
}

An example that two objects are equal is like the following:

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

Upvotes: 15

Andrea Murru
Andrea Murru

Reputation: 118

Do you know how comparison protocols works on iOS?

If you don't, check here http://nshipster.com/swift-comparison-protocols/

Basically if you do

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

You should create a class like this conform to 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

I suggest to check .text for testing

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

Upvotes: -4

Related Questions