4

When trying to downcast a fetched NSManagedObject to it's correct subclass StoryPhotoTrack in my (Swift) unit tests, I get a runtime error: Could not cast value of type 'StoryPhotoTrack_StoryPhotoTrack_' (0x7fbda4734490) to 'StoryPhotoTrack' (0x11edd1b08).

Here's my failing / crashing test:

func testFetchingStoryWithCastingInvolved() {
    let story : StoryPhotoTrack? = storyFetcher.fetchAnyStoryPhotoTrack()
    XCTAssertNotNil(story, "should be a story") // works
    let definitlyAStory : StoryPhotoTrack = story! // works
    let object : NSManagedObject = story as NSManagedObject! // works
    println(NSStringFromClass(definitlyAStory.dynamicType)) // prints 'StoryPhotoTrack_StoryPhotoTrack_'
    let downCastedStory : StoryPhotoTrack = object as! StoryPhotoTrack // -> error: "Could not cast..."
    XCTAssertNotNil(downCastedStory, "should still be a story")
}

// StoryFetcher.swift
    func fetchAnyStoryPhotoTrackVia_ObjectiveC_Fetcher() -> StoryPhotoTrack? {
        let object = StoryPhotoTrack.fetchAnyStoryPhotoTrackInContext(moc)
        return object as StoryPhotoTrack?
    }

The actual fetching is done in Objective-C:

// StoryPhotoTrack+Fetching.m
+ (StoryPhotoTrack *)fetchAnyStoryPhotoTrackInContext:(NSManagedObjectContext *)moc
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"StoryPhotoTrack"];
    NSArray *storyTracks = [moc executeFetchRequest:request error:nil];
    return (StoryPhotoTrack *)[storyTracks lastObject];
}

I assume, it has something to do with Swift's namespaces. Seems like my StoryPhotoTrack class is actually called StoryPhotoTrack_StoryPhotoTrack_.

I tried renaming the class names of my core data entities with projectName-prefixes (as suggested here), but that did not help. I tried both my main target module name and my test target module name, none of them worked. setting class prefix in core data model my testbundle module name

What am I doing wrong?

edit: seems like I overlooked SO questions like How to test Core Data properly in Swift, so it seems, this issue is known, but not easy to solve. Is it really the best way to create a separate framework for your Core Data Model?

Community
  • 1
  • 1
Goodsquirrel
  • 1,476
  • 1
  • 15
  • 29

1 Answers1

0

I had the same issue, for some reason, I was not able to cast to my custom NSManagedObjects for unit test, the only solution was to keep the result set as NSManagedObject Array and access it is fields as below without casting it to ProductModel, object in itself is still a ProductModel but casting it cause an error.

I kept my model name as ProductModel without MyAppName.ProductModel.

     func testProductModel() {
            let resultFetchRequest = NSFetchRequest(entityName: ProductModel.Name)
            let productModels: [NSManagedObject] = context.executeFetchRequest(resultFetchRequest, error: &error) as!
                    [NSManagedObject]

            if let recipes = productModels[0].valueForKey("recipes") as? NSSet {
                let recipesCount = recipes.count;
                XCTAssertTrue(recipesCount == 0)
            } else {
                XCTFail("FAIL - no recipes nsset")
            }
        }
kmarin
  • 332
  • 3
  • 8
  • Ok, thank you, this will probably be the last resort workaround. But I'm still hoping for an answer / setup that solves the namespace problem and actually allows casting. – Goodsquirrel Jun 23 '15 at 13:16
  • 1
    I have tried few variations , one solution was, not to add any class file to test targets, and making all the class and functions public, i was able to cast it to any custom model, but it was not ideal. – kmarin Jun 23 '15 at 15:29