How to write unit test for method which instantiates collaborators inside itself?

52 Views Asked by At

Method code like so:

- (void)downloadSomething:(NSString *)url {
    Downloader *downloader = [[Downloader alloc] initWithUrl:url];
    NSData *data = [downloader download];
    FileCache *cache = [[FileCache alloc] initWithFile:@"download.cache"];
    cache.data = data;
    [cache save];
}

I think I should mock Downloader and FileCache to verify if it works well. I have thought about change signature like that: downloadSomething:(NSString *)url downloader:(Downloader *)downloader cache:(FileCache *)cache, but it seem to have to do much work before call this methods, that's not what I want.

I am using ocmockito.

Besides, is there a guide to make writing code more testable ?


edit: 2017-01-16 14:54:23

Is this a good idea to write two method like:

- (void)updateCacheWithUrl:(NSString *)url 
                downloader:(Downloader *)downloader 
                 fileCache:(FileCache *)fileCache; // for testing
- (void)updateCacheWithUrl:(NSString *)url; // call above method with (url, nil, nil);
1

There are 1 best solutions below

7
On BEST ANSWER

When a collaborator is instantiated inside a method, this creates tight coupling. The difficulty of testing this leads us to explore other designs.

One way is to pass them in. This is what I'd normally do. Then I would make a simpler version that provides default objects for production code.

But in your example, the url is passed to the Downloader, which makes this harder. This suggests that the current design of downloadSomething: violates the Single Responsibility Principle. It's doing two things: downloading, and caching.

So splitting up these responsibilities would probably make things easier to test.