Mocking Objective-C code in Swift Unit-Tests - objective-c

NOTE: This isn't my actual code, I re-typed it in a simple fashion on to here for your help.
I have this class written in Objective-C
#interface MySpecialManager <NSObject>
+ (MySpecialManager *)sharedInstance;
-(void)someFunctionWithParamOne:(NSString *)paramOne andParamTwo:(NSString *)paramTwo;
-(void)someFunctionWithParamOne:(NSString *)paramOne andParamTwo:(NSString *)paramTwo success:(void (^)(NSString *))success failure:(void (^)(NSError *error))failure;
#end
I have unit tests (in Swift) for the second function with the success/failure blocks but I am now trying to write unit tests for the first function. All this function does is call the second function. Therefore, I was thinking the best way to test this would be to just check that the second function does indeed get called and with the correct parameters. Therefore, I thought mocking/stubbing was the way to go forward but I am struggling to understand how exactly to unit test this.
From much Googling I read that creating my own Mock object would be the way to go forward so I have this now (written in Swift):
class MockMySpecialManager: NSObject, MySpecialManagerProtocol {
var functionOneWasCalled = false
var functionTwoWasCalled = false
func someFunctionWithParamOne(paramOne: String!, andParamTwo paramTwo: String!) {
functionOneWasCalled = true
}
func someFunctionWithParamOne(paramOne: String!, andParamTwo paramTwo: String!, success: ((String!) -> Void)!, failure: ((NSError!) -> Void)!) {
functionTwoWasCalled = true
}
If in my test though I initialise a MockMySpecialManager and call the first method, it won't call the second one as it just updates the boolean, correct? I could update the mock function to call the second but that feels like cheating and not a real unit test as its not testing my code. How can I test this?
I somehow (or so I think) need to set the manager to MySpecialManager.sharedInstace(), call the first method and then check if my second method was called on the mock.
Any help? What am I misunderstanding/where am I going wrong?

Your current MOC class is actually a complete replacement of the target class, so you aren't actually testing it at all.
If your MOC was a subclass of the target class instead, and only implemented the second method, then the test can call the first method and the MOC can verify that the second method was called.
Often you would use a mocking library to assist with this, and those libraries allow you different ways to do the same thing as above.
Alternatively you wouldn't MOC the target class, you would MOC all of its dependencies. Then your test checks that the dependencies are called appropriately and with the requisite parameters. Then your first and second method tests are the same setup but with slightly different expectations.

Related

Mock NSCache Generic Methods in Swift

I'm trying to mock NSCache by building a protocol that NSCache can conform to in order to use dependency injection. I can't seem to figure out how or if you can add the generic methods to your protocol. For example, NSCache has this method:
open func removeObject(forKey key: KeyType)
So if in my protocol I try to declare:
func removeObject<KeyType: AnyObject>(forKey key: KeyType)
Xcode says NSCache doesn't fulfill the protocol and when I try to click on the FixIt to add the stubs, it then adds:
func removeObject<KeyType>(forKey key: KeyType) where KeyType : AnyObject {
}
Then that line has an error message:
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime.
How can I mock something like this?
Your attempt is doomed to fail, due to the way the Objective-C generics work, they aren't 100% compatible with the Swift generics. You'll need to encapsulate NSCache within a Swift class, and mock that one instead if you want to go this road.
But I want to argue the fact that you need a mock here. Your NSCache instance works like a database, so why not injecting it directly instead of a protocol and assert based on the contents of the cache after performing some operations?
For example
class MyClassThatNeedsACacheTests: XCTestCase {
let nsCache = NSCache<Key, Value>()
let sut = MyClassThatNeedsACache(cache: nsCache)
func testBehaviorWhenCacheIsEmpty() {
sut.doSomething()
// assert what is needed
}
func testBehaviorWithCachedData() {
nsCache.setObject(someValue, forKey: somekey)
sut.doSomethingElse()
// assert what is needed
}
}
As a general rule, I'd recommend to use mocking as the last resort. Mocking tightly couples the tests and the implementation they test, as you'll have to keep the tests in sync with the implementation. If you have the possibility, input->output tests are the most reliable and most robust tests.

Recursive looking Swift extension func, actually isn't. Why not?

I am looking at the Swift code of the ThemeKit theming library.
In particular I would like to understand the following code in NSColor+ThemeKit.swift:
// ThemeKit.set() replacement to use theme-aware color
#objc public func themeKitSet() {
// call original .set() function
themeKitSet()
// check if the user provides an alternative color
if ThemeManager.shared.isEnabled && isThemeOverriden {
// call ThemeColor.set() function
ThemeColor.color(with: Selector(colorNameComponent)).set()
}
}
There is what appears to be an endless recursive call, but presumably can't be, since the code works fine. This is confirmed by setting a breakpoint on the call to themeKitSet(). It is not possible to step into the call and execution continues without recursion.
Earlier in the file there is the following call:
swizzleInstanceMethod(cls: NSClassFromString("NSDynamicSystemColor"), selector: #selector(set), withSelector: #selector(themeKitSet))
With the implementation in NSObject+ThemeKit.swift as follows:
/// Swizzle instance methods.
#objc internal class func swizzleInstanceMethod(cls: AnyClass?, selector originalSelector: Selector, withSelector swizzledSelector: Selector) {
guard cls != nil else {
print("Unable to swizzle \(originalSelector): dynamic system color override will not be available.")
return
}
// methods
let originalMethod = class_getInstanceMethod(cls, originalSelector)
let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector)
// add new method
let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
// switch implementations
if didAddMethod {
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
I suspect this is responsible for the magic, but my limited understanding of both Swift and Objective-C is letting me down.
What is happening here? Why is the apparently recursive call not actually recursive?
You correctly identified the magic bit: it's called method swizzling, and it's a way of wholesale replacing an existing method implementation.
You'll see this seemingly-recursive pattern a lot when method swizzling: that themeKitSet call actually runs the original implementation, as the comment says. It's because swizzling swaps the implementations of two methods, in this case themeKitSet and NSDynamicSystemColor.set.
Therefore, post-swizzle, NSDynamicSystemColor.set runs the code you see there, and themeKitSet has become the original implementation.

Swift class properties not initialized when constructed by Objective C code

I'm attempting to create a class in Swift 3 to implement a Cordova plugin. I have this building and running, but the application crashes whenever any properties of the class are accessed. I've tried two ways of initializing the class:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players = [UUID:DSFPlayerHandler] ();
...
}
and
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler];
override init () {
players = [:];
}
...
}
However, when my players property is used, the result is a EXC_BAD_ACCESS exception, with an address that looks like a null pointer dereference.
The object is being created by Objective C code, which is a language I have no familiarity with at all, but I think this is the line that creates it:
obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
The CDVPlugin class contains a comment stating that initWithWebViewEngine should not be overridden (and indeed I do not seem to be able to override this method, because while it is declared in the CDVPlugin.m file, it isn't mentioned in CDVPlugin.h, so the Swift compiler doesn't seem to know about it), but rather initialization code should be placed in a method called pluginInitialize instead. However, if I do that I get a compiler error ("Class DSFMediaCentre has no initializers").
Furthermore, if I put my init() method back in and set it to call pluginInitialize(), like this:
override init () {
super.init(); // necessary otherwise next line is an error
pluginInitialize();
}
override func pluginInitialize() {
players = [:];
}
the error then changes to "Property 'self.players' not initialized at super.init call".
How do I make this class initialize correctly?
You have a mismatch between the strict initialization system required by the language and the procedure used by the framework you're working with.
Swift demands that a) properties be initialized as part of object construction, and b) that construction be chained to the type's supertype. But the CDVPlugin type is doing the construction on your behalf; you don't have the ability to customize it. (This makes more sense in ObjC, because it doesn't have the same compile-time restrictions as Swift.)
The situation is similar to unpacking an object from a nib file. In that case too, because it's the nib loading system that's constructing your object, you don't have the ability to customize the initializer. Your type will always be constructed by init(coder:). In a certain sense, your initialization point moves further down, to awakeFromNib(), and among other things, that forces outlets to other objects in the archive to be declared as optional, usually implicitly unwrapped.
The same solution should avail you here. You should consider pluginInitialize() to be your initialization point. The language then requires that properties be optional, since they are not filled at its initialization point. Therefore, make the property an IUO:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler]!
override func pluginInitialize() {
players = [:];
}
}
and all should be well.
The other solution is to use lazy keyword
lazy var players :[UUID:DSFPlayerHandler] = [:]
So, you don't need to initialize players in initializer but still make sure players always non-nulable

A method can only be called after the object is initialized - how to proceed?

I am finding a recurring pattern in my day-to-day coding, as follows:
var foo = new Foo();
foo.Initialize(params);
foo.DoSomething();
In these cases, foo.Initialize is absolutely needed so that it can actually DoSomething, otherwise some foo properties would still be null/non-initialized.
Is there a pattern to it? How to be safely sure DoSomething will only/always be called after Initialize? And how to proceed if it doesn't: should I raise an exception, silent ignore it, check some flag...?
Essentially you're saying Initialize is a constructor. So that code really should be part of the constructor:
var foo = new Foo(params);
foo.DoSomething();
That's exactly what a constructor is for: it's code which is guaranteed to run before any of the object methods are run, and its job is to check pre-conditions and provide a sane environment for other object methods to run.
If there really is a lot of work taking place in the initialization, then I can certainly see the argument that it's "too much to put in a constructor". (I'm sure somebody with a deeper familiarity of language mechanics under the hood could provide some compelling explanations on the matter, but I'm not that person.)
It sounds to me like a factory would be useful here. Something like this:
public class Foo
{
private Foo()
{
// trivial initialization operations
}
private void Initialize(SomeType params)
{
// non-trivial initialization operations
}
public static Foo CreateNew(SomeType params)
{
var result = new Foo();
result.Initialize(params);
return result;
}
}
And the consuming code becomes:
var foo = Foo.CreateNew(params);
foo.DoSomething();
All manner of additional logic could be put into that factory, including a variety of sanity checks of the params or validating that heavy initialization operations completed successfully (such as if they rely on external resources). It would be a good place to inject dependencies as well.
This basically comes down to a matter of cleanly separating concerns. The constructor's job is to create an instance of the object, the initializer's job is to get the complex object ready for intended use, and the factory's job is to coordinate these efforts and only return ready-for-use objects (handling any errors accordingly).

JMockit basics: mocked object, mocked parameter, expectation

I just started using JMockit and I am a bit confused on some basics of JMockit, in terms of when mock object is created, mocked object scope and what is the effect of mock etc. Please help with the following questions.
My questions refer to the following code:
public class MyClassTest
{
#Mocked({"method1","method2"})
ClassA classA; //ClassA has only static method
#Mocked
ClassB classB;
#Test
public void test1()
{
new NonStrictExpectations() {{
MyClassA.method3(classB);
result = xxx;
}};
// testing code
....
// verification
...
}
#Test
public void test2(#Mocked ClassC classC)
{
...
}
}
Questions:
About #Mocked declared as a instance variable for a junit test, like #Mocked ClassB classB:
(1) For junit, the instance variable is newly created for each test (like test1(), test2()), right? Is it true that before each test runs, a new mocked instance of ClassB is created?
(2) It mocks the class. It makes all methods in ClassB mocked for all tests (test1() and test2() in this case), right?
(3) If methods are specified for mocked object, like "#Mocked({"method1","method2"}) ClassA classA;", it means only method1 and method2 can be mocked? Can other methods be added to be mocked in Expectations for a test?
About #Mocked parameter passed in to test, like "#Mocked ClassC classC" for test2:
I assume this mock should not affect other tests? Is it true that ClassC is only mocked in test2()?
Expectation:
(1) For expectation specified in a test, is its scope local to the test, meaning the mocked method is only effective in this test? For example, ClassA.method3() is only mocked in test1(), right?
(2) The recorded method in expectation only runs when the matching method is invoked from the testing code, is it? If recorded method parameter does not match, will it run the real method?
I am getting an exception in ClassA.method3() when running test1(). Somehow the real method of ClassA.method3() is executed and gave exception. I guess it is due to parameter mismatch for ClassA.method3()?
Answering your questions:
(1) Yes; (2) yes; (3) yes, and other methods cannot be mocked in this same test class.
Yes, only in the test which has the mock parameter.
(1) Right, the expectation is only valid within the scope where it's recorded. (2) No, once mocked, the real implementation of a method is never executed.
As for the exception you get, I can't tell why it happens without seeing a complete test.