1

i have got simple (i guess) question.

I want to make a feature test in my app with Specta and KIF. The problem is that i am setting dependency in viewDidLoad method of my View Controller, and in beforeEach method of my spec i'm injecting fake object just to not hit the network.

The result is wrong because viewDidLoad is being called before beforeEach method in specs.

Is there a possibility to set dependencies before AppDelegate loads root view controller so everything is set up properly?

Konrad Szczęśniak
  • 1,960
  • 1
  • 14
  • 13

1 Answers1

5

Tests that depend on a target (like KIF and certain unit tests) launch after the app is launched so no, you can not make the beforeEach go before your AppDelegate without some terrible hackery.

I don't know how you're doing dependency injection so here's how we do it/some general strategies.

KIF tests should ideally not mock at the code level

This is because KIF is kind of an alternative to UIAutomation and mainly useful for feature/functional tests at the UI level. You don't really want to change your application code so much. Mocks are best achieved using frameworks like OHHTTPStubs for network or OCMock for objects and those work best when limited to unit tests.

How to mock out network requests in a "real" app

The best way here is to use something like OHHTTPStubs or AMY server (made by same people that made KIF) or Nocilla to return stub responses. This way you can let your app code run fully. OHHTTPStubs for example uses NSURLProtocol to intercept your requests so from the app perspective it's almost as good as going out to the network.

I really want to mock out those objects

If you really really really want to mock out dependency injected objects with different objects then there are a couple of more and less hacky options.

1) Use a real DI framework (or build your own) that allow for patching of the dependencies. I've used Typhoon and it's reasonable. The standard idea here is to use Inversion of Control to your advantage. Since you're getting all your objects from an application context rather than directly, it's much easier to tweak the application context abstraction layer. Typhoon even has a wiki page on this topic: https://github.com/appsquickly/Typhoon/wiki/Integration-Testing

2) Trace the source of the object you're injecting and want to mock out and change it at the source. This isn't the most elegant and you're sort of hacking out a DI framework anyway but maybe you don't have enough time or complexity to make switching to a DI framework worth it.

3) Hack all the way to the top level. Have a test AppDelegate that subclasses from your normal AppDelegate and use that during KIF testing (and of course have it stub out or mock out objects you want). This isn't flexible but again, maybe you just want one test case or something:

int main(int argc, char *argv[])
{
    int returnValue;
    @autoreleasepool {
        BOOL inIntegrationTests = NSClassFromString(@"KIFTestCase") != nil;
        if (inIntegrationTests) {
            returnValue = UIApplicationMain(argc, argv, nil, @"AppDelegateForTest");
        }
        else {
            returnValue = UIApplicationMain(argc, argv, nil, @"AppDelegate");
        }
    }
    return returnValue;
}

Ultimately this isn't a simple "where do I put this method" question unfortunately.

plluke
  • 1,905
  • 11
  • 9
  • Thank you for your extensive answer. We'll have to mock out some objects as we want to have functional tests run on Travis. Apart from stubbing HTTP requests out, we need to do the same for other things, e.g. location service. I have also put a bounty on [this related question](http://stackoverflow.com/questions/22560990/how-to-mock-location-service-using-kif-framework). – MaciejGórski Feb 18 '15 at 07:31
  • @MaciejGórski I've answered that question as well. Gave two approaches as I wasn't sure of the qualities of Location Service. If it's CLLocationManager then the first pattern would work fine. If it's some other location service with a singleton, then you need to get slightly fancier and use the second method. I would highly suggest something like Typhoon though since it fits this kind of case (needing to mock things out during integration testing) perfectly. – plluke Feb 18 '15 at 22:38
  • @plluke Does this work for you on Xcode 7 beta too? I use a similar thing for unit testing, but on Xcode 7 beta 6 the test delegate is not found and test target crashes. – AShavit Sep 01 '15 at 14:35
  • Sorry if this is not totally related, but plluke's answer helped me fix my KIF configuration so I thought I might be able to help someone. I was running into this issue here: https://stackoverflow.com/questions/18538958/all-kif-test-steps-are-throwing-the-same-error-what-am-i-doing-wrong where basically KIF can't find a key window for the app (and throws a not-too-helpful exception because of it). If you swap in AppDelegate instances based on test environment like plluke does above, it's possible you're giving KIF an AppDelegate configuration that doesn't create a proper key window. – Brian Sachetta Aug 31 '18 at 19:22