realm / realm-swift

Realm is a mobile database: a replacement for Core Data & SQLite
https://realm.io
Apache License 2.0
16.31k stars 2.14k forks source link

Schema without Objects in Tests #690

Closed martintsch closed 10 years ago

martintsch commented 10 years ago

Hi, we are using realm in our application and everything is working fine. We have 3 Objects in our schema.

When trying to use realm in tests we have the issue, that we can not persist. The schema is empty.

Image Xcode

It seems that we need to initialize the schemas in some other way in tests? Is there some best practise for that?

Object type 'MPRCompany' not persisted in Realm
(
    0   CoreFoundation                      0x0288e1e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0260d8e5 objc_exception_throw + 44
    2   SetaTests                           0x0b158533 -[RLMSchema objectForKeyedSubscript:] + 218
    3   Seta                                0x0009aa7b _Z19RLMAddObjectToRealmP9RLMObjectP8RLMRealm + 278
    4   SetaTests                           0x0b1568cf -[RLMRealm addObject:] + 24
    5   SetaTests                           0x0b115679 __32-[MPRCompanySpec spt_defineSpec]_block_invoke184 + 201
    6   SetaTests                           0x0b25798c runExampleBlock + 1500
    7   SetaTests                           0x0b258fd2 __48-[SPTExampleGroup compileExamplesWithNameStack:]_block_invoke + 258
    8   SetaTests                           0x0b25e35c -[SPTXCTestCase spt_runExampleAtIndex:] + 556
    9   CoreFoundation                      0x0288291d __invoking___ + 29
    10  CoreFoundation                      0x0288282a -[NSInvocation invoke] + 362
    11  XCTest                              0x20103c6c -[XCTestCase invokeTest] + 221
    12  XCTest                              0x20103d7b -[XCTestCase performTest:] + 111
    13  SetaTests                           0x0b25ed48 -[SPTXCTestCase performTest:] + 152
    14  XCTest                              0x20104c48 -[XCTest run] + 82
    15  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    16  XCTest                              0x20104c48 -[XCTest run] + 82
    17  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    18  XCTest                              0x20104c48 -[XCTest run] + 82
    19  XCTest                              0x201033e8 -[XCTestSuite performTest:] + 139
    20  XCTest                              0x20104c48 -[XCTest run] + 82
    21  XCTest                              0x201066ba +[XCTestProbe runTests:] + 183
    22  Foundation                          0x00a215ec __NSFireDelayedPerform + 372
    23  CoreFoundation                      0x0284cac6 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
    24  CoreFoundation                      0x0284c4ad __CFRunLoopDoTimer + 1181
    25  CoreFoundation                      0x02834538 __CFRunLoopRun + 1816
    26  CoreFoundation                      0x028339d3 CFRunLoopRunSpecific + 467
    27  CoreFoundation                      0x028337eb CFRunLoopRunInMode + 123
    28  GraphicsServices                    0x03e7b5ee GSEventRunModal + 192
    29  GraphicsServices                    0x03e7b42b GSEventRun + 104
    30  UIKit                               0x01227f9b UIApplicationMain + 1225
    31  Seta                                0x00011262 main + 178
    32  libdyld.dylib                       0x02e7f701 start + 1
) 
alazier commented 10 years ago

Schema are created for any objects which derive from RLMObject in your binary. Are you compiling the 3 object classes you wish to test in your test binary? It's hard to tell from the callstack.

martintsch commented 10 years ago

We have our application, which is a target dependency in our Tests.

The compile Sources in the Tests just contain the tests. When we debug and have a breakpoint on the defaultRealm, in the application code, we see that the schema contains all data.

The defaultRealm called in the tests has no schema. So I guess we have 2 different realms here. The question would be how we can access the application realm out of the tests?

Would we need some kind of bridging which would expose the data?

alazier commented 10 years ago

You need to compile your object classes into your tests as well. Once you do this your tests with have the same schema as your application.

ajimix commented 10 years ago

Hello, when we add the object classes to be compiled we get this error: Terminating app due to uncaught exception 'RLMException', reason: 'Property of type 'MPRCompany' must descend from RLMObject' To clarify, MPRCompany is a property (relation) from another object.

The weird thing is that the problem only occurs in the tests. Everything is working fine in the app, objects, relations, reading, writing, etc.

alazier commented 10 years ago

I tried to reproduce the issue you are seeing but haven't been able to do so - I created two model objects, one which has the other as a property and as an array, and successfully compiled and used the objects both in an app and in tests.

If you could share a full callstack I might be able to better narrow down the issue. Sharing your data models might also be helpful.

On Wed, Jul 30, 2014 at 12:46 AM, Adria Jimenez notifications@github.com wrote:

Hello, when we add the object classes to be compiled we get this error: Terminating app due to uncaught exception 'RLMException', reason: 'Property of type 'MPRCompany' must descend from RLMObject' To clarify, MPRCompany is a property (relation) from another object.

The weird thing is that the problem only occurs in the tests. Everything is working fine in the app, objects, relations, reading, writing, etc.

— Reply to this email directly or view it on GitHub https://github.com/realm/realm-cocoa/issues/690#issuecomment-50584168.

ajimix commented 10 years ago

We created a simple test project where we can reproduce the issue. You can take a look here: https://github.com/ajimix/realm-test

Thanks

jpsim commented 10 years ago

Hi @ajimix, there were a number of CocoaPods-related issues in your sample code project, which I've fixed in a PR: realm-test#1. Let me know if that works for you.

ajimix commented 10 years ago

Hello @jpsim thank you for your time, we tried persisting and we have the initial issue that @martintsch described. I pushed an example again here: https://github.com/ajimix/realm-test

Thank you

tgoyne commented 10 years ago

Looks like the issue is that both the application and the tests are linking in Realm and ending up with separate copies that only see the models defined in their own target. For now you can work around this by removing -framework Realm from Pods.xcconfig and adding it to RealmTest's linker flags (and not RealmTestTests's), and I'll take a look at fixing the podspec.

ajimix commented 10 years ago

Thank you it worked! But anytime we do pod install we have to manually remove again the line from Pods.xconfig We will be waiting for a fix :)

Thanks!

tgoyne commented 10 years ago

An ugly but less annoying workaround is to add the following to the bottom of your Podfile:

post_install do |installer_representation|
  # Replace -ObjC by -force_load
  path = installer_representation.sandbox_root + 'Pods.xcconfig'
  tmp = path.to_s + '.tmp'
  File.open(path, "r") do |f|
    File.open(tmp, "w") do |g|
      f.each_line do |line|
        line.sub! '-ObjC', '-force_load $(TARGET_BUILD_DIR)/libPods.a'
        g.puts line
      end
    end
  end
  File.rename(tmp, path)

  # Remove target dependency from Pods target
  installer_representation.project.targets.each do |target|
    target.dependencies.select! { |d| d.target.name != 'Pods-Realm' }
  end
end

(Based on code from https://github.com/CocoaPods/CocoaPods/issues/712)

ajimix commented 10 years ago

Thanks, it works but it's a bit ugly. We'll be waiting for #735 to be merged.

martintsch commented 10 years ago

Hi all, with the 0.84.0 release we thought that this issue is gone but unfortunately it is not. :crying_cat_face: The example here with the update: https://github.com/ajimix/realm-test is still valid.

Maybe we keep doing stuff wrong?

tgoyne commented 10 years ago

Ah, I forgot to actually document what #761 did. There's now a Realm.Headers subspec which includes only the public headers for Realm so that you can use Realm in your test targets without linking in a second copy of Realm, which you can use as follows:

target :MyApp do
    pod 'Realm'
end
target :MyAppTests do
    pod 'Realm.Headers'
end

CocoaPods's switch to dynamic libraries (to support Swift) should fix the issue entirely, but for now I think this is the best we can do.

martintsch commented 10 years ago

I forgot to update. That with the Headers everything started to work fine!

Thank you very much!

bofrese commented 10 years ago

I get [!] Unable to find a specification for Realm.Headers.

I have tried to replace with 'Realm/Headers'. Then pod install no longer complains, but now I get lots of duplicate symbol errors during linking....

I'm back with the 'ugly workaround' suggested by tgoyne earlier. After some struggle I managed to get that working again....

ajimix commented 10 years ago

Hello @bofrese I work with @martintsch and what we did was to use Realm in the app target and Realm/Headers (not Realm.Headers) in the test target. What we also did was to set both targets as exclusive so each pod is linked exclusively with each target. So we ended up with something like...

target 'MyApp', exclusive: true do
    pod 'Realm', '~> 0.84.0'
end

target 'MyAppTests', exclusive: true do
    pod 'Realm/Headers', '~> 0.84.0'
end

I hope this helps.

faken commented 10 years ago

@ajimix i have tried to setup my Podfile as descriped but the compilation of the test projected failed with the error: ld library not found for -ltightdb-ios

any suggestion would could be wrong with my setup?

ajimix commented 10 years ago

Then I'm sorry but I don't have any idea, perhaps @tgoyne can help here.

tgoyne commented 10 years ago

Make sure that Other Linker Flags contains $(inherited). Xcode helpfully doesn't add it by default, which results in the linker flags set in the podspec not having any effect if you've ever touched it.

rbaulin commented 9 years ago

@tgoyne I've also spent a good few hours in debugging and building a sample until I found this issue. Solution provided by @ajimix works perfectly. Probably should highlight the cocoapods testing recipe in README or realm docs?

Tip. Since CocoaPods uses Xcodeproj, this small script might be useful to separate test targets from app targets:

proj = Xcodeproj::Project.open('YouProj.xcodeproj')
all_targets = proj.targets.map {|t| t.name}
test_targets = all_targets.select {|name| name.downcase.include? "test"}
app_targets = all_targets - test_targets