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 = "help@realm.io"
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;
})")
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.
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
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)
}