How can I access a core data store from an Xcode UI test? - ios

We've got a suite of UI tests for our app written using KIF which I'd like to convert to use the new Xcode UI test framework.
Some of our current tests work like this:
Assert that there are no objects in a core data table
Do some stuff in the UI
Assert that there are some objects in the core data table
Cancel the thing we were doing in the UI
Assert that there are no leaked objects in the core data table
I need to look at the actual core data store, not just the UI, because the leaked rows wouldn't necessarily be shown in the UI. How can I access a core data store from an Xcode UI test?

The answer is that you can't, not without abusing signals. XCUITests are not intended to touch the metal; they are intended to exercise user facing behavior only.
The test that you describe sounds like a perfectly good candidate for a unit test, though!
UPDATE:
(based on comments from OP)
Well, as far as I can tell you have four options
you can create a backchannel that will use signals passing to break
the separation between XCUITest and the app's internals.
you can
build functionality to mock the UI interactions in your unit tests
so that you can validate against side effects.
you can add an
assertion and then exercise it manually.
you can file a Radar
asking for the functionality.

You can easily test against Core Data but your test you described does not make much sense. If you are cancelling the UI action then Core Data does not save to disk. When you mention "table" that means to me that you are looking on disk.
If you want to load an empty Core Data store, create some objects, verify they were created, delete them and confirm they were deleted; that can all be done in a UI test.
Start with no store on disk (or use an in-memory store)
Run your UI test
Do a fetch against Core Data and confirm objects are in memory
Perform your cancel code
Confirm code is removed from Core Data.
What part are you having an issue with?

Related

Need some knowledge on Core Data - NSManagedObject usages

Just a quick question regarding loading data into the app through Core Data - I have already implemented Core Data and app is pretty much running just as it was before I started using it.
In short, when app launches I am fetching all the Entities into a Manager script that stores each object into different arrays and when the app uses the objects the Manager script will pass these arrays to other scripts.
My question is, does this seem like a bad method while using Core Data? Should I just be fetching the Entities when the scripts want them or its fine 'pre-fetching' from the start? This data is limited (to a point) and doesn't change during runtime.
Also, since I have all the entities in different arrays in the Manager script, I am sorting/filtering these arrays by predicates when a script needs something done. (which means I am never using a Core Data fetch with predicate since all the data is already available in the Manager's arrays)
App is already done, was just trying to make performance improvements and by adding Core Data, there is a huge boost. This is kind of my last question (at the moment) and I'm not really sure on the answer since I have never used Core Data before.
Thank you
It is fine.
There are three main drawbacks to prefetching:
You can take up too much memory and crash.
If an entity is deleted and you keep a pointer to it, the app will crash when you try to use that managedObject
Loading might take a very long time if there is a lot of data.
You said "This data is limited (to a point) and doesn't change during runtime" so it appears that none of these issue apply to you.
That being said I am not sure that you are really getting any benefit from core data in this setup. Core data in general scales very well, but your setup will not. You could just as easily store all your information in a file and load it all into memory when the app starts. I trust that you getting other benefits from core data that are not stated in your question.

How to create snapshot of CoreData state?

Background story
I am developing a big iOS app. This app works under specific assumptions. The main of them is that app should work offline with internal storage which is a snapshot of last synchronized state of data saved on server. I decided to use CoreData to handle this storage. Every time app launches I check if WiFi connection is enabled and then try to synchronize storage with server. The synchronization can take about 3 minutes because of size of data.
The synchronization process consists of several stages and in each of them I:
fetch some data from the server (XML)
deserialize it
save it in Core Data
Problem
Synchronization process can be interrupted for several reasons (internet connection, server down, user leaving application, etc). This may cause data to be out-of-sync.
Let's assume that synchronization process has 5 stages and it breaks after third. It results in 3/5 of data being updated in internal storage and the rest being out of sync. I can't allow it because data are strongly connected to each other (business logic).
Goal
I don't know if it is possible but I'm thinking about implementing one solution. On start of synchronization process I would like to create snapshot (some kind of copy) of current state of Core Date and during synchronization process work on it. When synchronization process completes with success then this snapshot could overwrite current CoreData state. When synchronization interrupts then snapshot can be simply aborted. My internal storage will be secured.
Questions
How to create CoreData snapshot?
How to work with CoreData snapshot?
How to overwrite CoreDate state with snapshot?
Thanks in advice for any help. Code examples, if it is possible, will be appreciated.
EDIT 1
The size of data is too big to handle it with multiple CoreData's contexts. During synchronization I am saving current context multiple times to cleanup memory. If I do not do it, the application will crash with memory error.
I think it should be resolved with multiple NSPersistentStoreCoordinators using for example this method: link. Unfortunately, I don't know how to implement this.
You should do exactly what you said. Just create class (lets call it SyncBuffer) with methods "load", "sync" and "save".
The "load" method should read all entities from CoreData and store it in class variables.
The "sync" method should make all the synchronisation using class variables.
Finally the "save" method should save all values from class variables to CoreData - here you can even remove all data from CoreData and save brand new values from SyncBuffer.
A CoreData stack is composed at its core by three components: A context (NSManagedObjectContext) a model (NSManagedObjectModel) and the store coordinator (NSPersistentStoreCoordinator/NSPersistentStore).
What you want is to have two different contexts, that shares the same model but use two different stores. The store itself will be of the same type (i.e. an SQLite db) but use a different source file.
At this page you can see some documentation about the stack:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/InitializingtheCoreDataStack.html#//apple_ref/doc/uid/TP40001075-CH4-SW1
The NSPersistentContainer is a convenience class to initialise the CoreData stack.
Take the example of the initialisation of a NSPersistentContainer from the link: you can have the exact same code to initialise it, twice, but with the only difference that the two NSPersistentContainer use a different .sqlite file: i.e. you can have two properties in your app delegate called managedObjectContextForUI and managedObjectContextForSyncing that loads different .sqlite files. Then in your program you can use the context from one store to get current data to show to the user and you can use the context that use the other store with a different .sqlite if you are doing sync operations. When the sync operations are finally done you can eventually swap the two files and after clearing and reloading the NSPersistentContainer (this might be tricky, because you will want to invalidate and reload all managed objects: you are switching to an entirely new context) you can then show the newly synced data to the user and start syncing again on a new .sqlite file.
The way I understand the problem is that you wish to be able download a large "object graph". It is however so large that it cannot be loaded at once in memory, so you would have to break it in chunks and then merge it locally into to Core data.
If that is the case, I think that's not trivial. I am not sure I can think of direct solution without understanding the object relations and even then it may be really overwhelming.
An overly simplistic solution may be to generate the sqlite file on the backend then download it in chunks; it seems ugly, but it serves to separate the business logic from the sync, i.e. the sqlite file becomes the transport layer. So, I think the essence to the solution would be to find a way to physically represent the data you are syncing in a format that allows for splitting it in chunks and that can afterwards be merged into a sqlite file (if you insist on using Core data).
Please also note that as far as I know Amazon (https://aws.amazon.com/appsync/) and Realm (https://realm.io/blog/introducing-realm-mobile-platform/) provide background sync of you local database, but those are paid services and you would have to be careful not be locked in (should not depend on their libs in your model layer, instead have a translation layer).

XCTest with Core Data

Do XCTests run in parallel (that is, are multiple tests allowed to run at the same moment?). Consider a single test file with multiple tests, and multiple test files with multiple tests each.
If core data is used in some of the components being tested, and tests can run in parallel, what is the correct method of using the core data in the tests? For example, the test should start with a 'clean' data store, and then add objects as needed, then get tested based off of the contents of the store. It sounds like if they all use the same managed object context/store they'll be pointing to the same data and thus be at risk for colliding with each other.
Tests are run serially on the main thread of the test runner process. However nothing automatically guards against you starting asynchronous actions which could extend into the execution of a future test case.
For example a call to perfomBlock on a NSManagedObjectContext is not guaranteed to execute before the next test starts. This can be especially problematic if your tests trigger saves which propagate asynchronously to parent managed object contexts.
I've found it valuable to write easily testable code which means injecting the managed object contexts or other dependencies into the code under test. That should allow you to build an independent Core Data stack for each test case rather than unexpectedly sharing some global state in a single context. Then you just need to beware of overly permissive notification observers which don't bother to check the sender of a NSNotification (i.e. when observing NSManagedObjectContextDidSaveNotifications).
Each XCTest method is run sequentially (one at the moment)
To test Core Data I often create in memory Persistance Store, here you have good snipped: code using this kind of MOC you always have clear core data state
Please check also this Rays tutorial
1.Test cases are executed one by one and test files as well.
2.Your core data managed object context should be created by importing you app in your test case(#testable import product_name) file and access the core date objects in test case files.All the test cases will be run independently.
So As you mentioned the test should start with a 'clean' data store, and then add objects as needed, then get tested based off of the contents of the store.yes,this is correct way.make sure core data managed objects are created in test files.test cases can be tested in Test Navigation section.

Unit Testing with Kiwi, Core Data and Magical Record

I'm having issues using a 'fake' store for my Unit Tests.
I have installed Kiwi by adding its framework folder to my project and replacing the Xcode's default test cases with Kiwi tests. These all run fine.
Since I'm using Core Data, I need to create a 'fake' store so I'm playing with the real database. I used http://www.cimgf.com/2012/05/15/unit-testing-with-core-data/ as my basic guide to do this.
However, since Xcode's default test implementation runs tests after launching the app, my '[MagicalRecord setupCoreDataStackWithStoreNamed:#"Store.sqlite"]' is still fired inside the App Delegate before any of the tests run.
By the time the tests try to use '[MagicalRecord setupCoreDataStackWithInMemoryStore]', this sqlite store is set up, and so the in-memory store doesn't get set up (AFAIK), since the aforementioned setup stack method checks first to see if a stack already exists, and just returns without executing anything if it does, so I end up with the sqlite database still.
As far as I can tell, this leaves me with the following options:
Put some environment variables or flags in for the test cases, and check for these in the app delegate, creating the appropriate store depending on this variable (i.e. tweaking my actual code for the sake of testing - not pretty, nor recommended by any practising TDD/BDDers).
Add managed context properties on all my controllers so I can manually specify the store to use (removing a great deal of the niceties of the MagicalRecord singleton access pattern).
Play (carefully) with my actual database (I'm not really willing to even contemplate this).
None of these seems to be a particularly good solution, so I'm hoping someone can see a better solution that I've stupidly overlooked.
Your tests should not be launching the app delegate. Try setting up your tests so that only the tests setup the in-memory core data store, as suggested in the article you reference.

From UIManagedDocument to traditional Core Data stack

I created a new App using UIManagedDocument. While on my devices everything is fine, I got a lot of bad ratings, because there are problems on other devices :(
After a lot of reading and testing, I decided to go back to the traditional Core Data stack.
But what is the best way to do this with an app, that is already in the app store?
How can I build this update? What should I take care of?
Thanks,
Stefan
I think you may be better off to determine your issues with UIManagedDocument and resolve them.
However, if you want to go to plain MOC, you only have a few things to worry about. The biggest is that the UIMD stores things in a file package, and depending on your options you may have to worry about change logs.
In the end, if you want a single sqlite file, and you want to reduce the possibility of confusion, you have a class that simply opens your UIManagedDocument, and fetches each object, then replicates it in the single sqlite file for your new MOC.
Now, you should not need a different object model, so you should not have any migration issues.
Then, just delete the file package that holds the UIManagedDocument, and only use your single file sqlite store.
Basically, on startup, you try to open the UIManagedDocument. If it opens, load every object and copy it into the new database. Then delete it.
From then, you should be good to go.
Note, however, that you may now experience some UI delays because all the database IO is happening on the main UI thread. To work around this, you may need to use a separate MOC, and coordinate changes via the normal COreData notification mechanisms. There are tons of documents, examples, and tutorials on that.
EDIT
Thanks for your answer. My problem with these issues is, that I'm not
able to reproduce them. All my Devices are working fine. But I got a
lot mails, about problems like this: - duplicate entries - no data
after stoping and restarting the app - some say, that the app works
fine for some days and stops working(no new data). These are all
strange things, that don't happen on my devices. So for me the best
way is to go back to plain MOC. My DB doesn't hold many user generated
data, all the data is loaded from a webservice, so it's no problem to
delete the data and start of using a new DB. – Urkman
Duplicate entries. That one sounds like the bug related to temporary/permanent IDs. There are lots of posts about that. Here is one: Core Data could not fullfil fault for object after obtainPermanantIDs
Not saving. Sounds like you are not using the right API for UIManagedDocument. You need to make sure to not save the MOC directly, and either use an undo manager or call updateChangeCount: to notify UIManagedDocument that it has dirty data that you want to be saved. Again, lots of posts about that as well. Search for updateChangeCount.
However, you know your app best, and it may just be better and easier to use plain Core Data.
Remember, if you are doing lots of imports from the web, to use a separate MOC, and have your main MOC watch for DidSave notifications to update itself with the newly imported data.
UIManagedDocument is a special kind of document, an UIDocument subclass, that stores its data using Core Data Framework. So it combines the power of document architecture and core data capabilities.
You can read more about document based architecture from Document Based App Programming Guide for iOS and I recommend WWDC2011 Storing Documents in iCloud using iOS5 session video. I also recommend Stanford CS193P: iPad and iPhone App Development (Fall 2011) Lecture 13.
What is created when you call saveToURL:forSaveOperation:completionHandler: is an implementation detail of UIManagedDocument and UIDocument and you should not really worry or depend on it. However in current implementation a folder containing an sqlite database file is being created.
No. All entities will be contained in a single database file also more generally called: a persistent store. It is possible to use more than one persistent store, but those are more advanced use cases and UIManagedDocument currently uses one.
UIManagedDocument's context refers to a NSManagedObjectContext from underlying Core Data Framework. UIManagedDocument actually operates two of those in parallel to spin off IO operations to a background thread. When it comes to the nature of a context itself here's a quote from Core Data Programming Guide:
You can think of a managed object context as an intelligent scratch pad. When you fetch objects from a persistent store, you bring temporary copies onto the scratch pad where they form an object graph (or a collection of object graphs). You can then modify those objects however you like. Unless you actually save those changes, however, the persistent store remains unaltered.
But it really is a good idea to take a look at the lectures and other material I posted above to get a general picture of the technologies used and their potential value to you as a developer in different situations.

Resources