Mock NSCache Generic Methods in Swift - objective-c

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.

Related

Kotlin static factory method with generics

Found something similar with what I want to achieve for java - java generics and static methods also implementing factory pattern with generics in java from baeldung.
In my case, I also want to have the factory as a static method, but not sure how to achieve it in Kotlin, or maybe the whole concept is wrong.
Shortly, there are certain types of Notifiers, each should handle a certain NotificationInput type. Basically they are also some kind of builders as they build up the Notification object from the input.
Considering the setup below, I get Type mismatch. Required: Notifier<T> Found: DebugNotifier (same for the other Notifier implementations).
interface Notifier<T> {
fun issue(p: NotificationInput<T>): Notification
companion object {
fun <T> getNotifier(p: NotifierParameter): Notifier<T> = when(p.type){
"0" -> DebugNotifier()
"1" -> InfoNotifier()
"2" -> ErrorNotifier()
}
}
class DebugNotifier: Notifier<Debug>{
override fun issue(p: NotificationInput<Debug>): Notification{
return Notification(
somField = p.someDebugFieldValue
)
}
}
data class NotificationInput<T>(
val data: T
)
This is how I plan to call it: Notifier.getNotifier<Debug>(notifierParameter).issue(notificationInput)
How can this be achieved, or what would be a better configuration?
As #broot already explained, the caller has control over 2 things here: the type argument T and the NotifierParameter argument, so the API is kinda broken because the caller could do:
Notifier.getNotifier<Debug>(NotifierParameter("2"))
What would you expect to happen here?
There are too many degrees of freedom in the inputs of getNotifier(), so the compiler cannot allow you to return ErrorNotifier() when you receive "2", because someone could pass <Debug> as type argument.
You cannot compare this kind of API with Java, because Java's generics are broken and allow things that don't make sense.

Passing a Swift generic protocol argument to an Obj-C “Protocol” function

I have a class that needs to pass a Protocol to an Obj-C function. I have a constructor that takes the Protocol, but as the class is a generic that also takes the same protocol, I thought I could optimise it. However, if I try to use the generic value when calling the function, it fails to compile. I've tried various combinations of ".self" and ".Type" and ".Protocol", both in the code and in the generic argument, and nothing works. Is there any way to achieve this?
This is a Playground project to show the problem.
import Foundation
#objc protocol TestProtocol {
}
class Test<P> {
init() {
test(p: P.self) // Fails to compile with: Cannot convert value of type 'P.Type' to expected argument type 'Protocol'
test(p: TestProtocol.self) // Compiles
}
func test(p: Protocol) {
}
}
let c = Test<TestProtocol>()

'this' is not defined in this context

How can I solve the following case?
interface I
class A(i: I)
class C : I, A(this) // << --- 'this' is not defined in this context
In short, I want to pass the class instance to super class constructor.
Is it possible in Kotlin?
P.S.
All the answers are good and technically correct. But let's give a concrete example:
interface Pilot {
fun informAboutObstacle()
}
abstract class Car(private val pilot: Pilot) {
fun drive() {
while (true) {
// ....
if (haveObstacleDetected()) {
pilot.informAboutObstacle()
}
// ....
}
}
fun break() {
// stop the car
}
}
class AutopilotCar : Pilot, Car(this) { // For example, Tesla :)
override fun informAboutObstacle() {
break() // stop the car
}
}
This example don't look too contrived, and why can't I implement it with OOP-friendly language?
No, this is not possible on the JVM. this is only available after the super class has been initialized.
From
https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.10.2.4
The instance initialization method (§2.9.1) for class myClass sees the new uninitialized object as its this argument in local variable 0. Before that method invokes another instance initialization method of myClass or its direct superclass on this, the only operation the method can perform on this is assigning fields declared within myClass.
So the bytecode instruction aload 0 to push this on the stack is forbidden before the super-class constructor is called. That's why it cannot be passed as an argument to the super-constructor.
Kotlin was born as a JVM language and aims for maximum interoperability with Java code and a minimum overhead of its language features. While Kotlin could have chosen to orchestrate object initialization in a different way, it would create problems in mixed Java-Kotlin class hierarchies and add significant overhead.
In the good tradition of OOP languages such as Java, C# or Swift, Kotlin doesn't allow you to leak the this reference before the call to superclass initialization has completed. In your special case you're just storing the reference, but in just a slightly different case the superclass code might try to use the received object, which at that point is still uninitialized.
As a specific example of why languages don't allow this, consider a case where A is a class from a library you use and this rule is not in effect. You pass this like you do and things work fine. Later you update the library to a newer version and it happens to add something as benign as i.toString() to its constructor. It has no idea it's actually calling an overridden method on itself. Your toString() implementation observes all its invariants broken, such as uninitialized vals.
This design suffers from other problems, not just the circular initialization dependency you are struggling with now. In a nutshell, the class A expects this:
But instead you create this:
The class A has a dependency on a collaborator object of type I. It doesn't expect itself as the collaborator. This may bring about all kinds of weird bugs. For example your C.toString() may delegate to super.toString() and A.toString() (A is the super of C) may call into I.toString(), resulting in a StackOverflowError.
I can't say from your question whether A is designed for extension, which would make the C : A part correct, but you should definitely disentangle A from I.

Calling a Method in Swift Unit Tests

My previous approach in Objective-C for unit testing was like following:
Calling a public method: no problem
Calling a private method: create a category of that class in your unit test file and put the signature of the private method into this category
Currently, I have the following:
Class developed in ObjC
Protocol developed in Swift
Unit tests for that class developed in Swift
The class conforms (directly, in its public interface (.h)) to that protocol. I have a class instance in my unit tests but somehow I can not invoke a method declared in the protocol over that instance.
Now, the old Category solution does not work with Extensions. When I put some method signature in the extension I get that error saying "Function body expected in declaration".
How can I call the functions with Swift in the best way?
PS: I do not want to declare the method again in the public interface of the class, that is an ugly solution.
As per the documentation on "Writing Tests with Swift" here:
Xcode provides a two-part solution to this problem:
When you set the Enable Testability build setting to Yes, which is
true by default for test builds in new projects, Xcode includes the
-enable-testing flag during compilation. This makes the Swift entities declared in the compiled module eligible for a higher level of access.
When you add the #testable attribute to an import statement for a
module compiled with testing enabled, you activate the elevated access
for that module in that scope. Classes and class members marked as
internal or public behave as if they were marked open. Other entities
marked as internal act as if they were declared public.
Here is also the example (for MySwiftApp)
Class built for a target with Enable Testability set to yes
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func foo() {
println("Hello, World!")
}
}
Unit test class
// Importing XCTest because of XCTestCase
import XCTest
// Importing AppKit because of NSApplication
import AppKit
// Importing MySwiftApp because of AppDelegate
#testable import MySwiftApp
class MySwiftAppTests: XCTestCase {
func testExample() {
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
appDelegate.foo()
}
}
Also see the important Note
Note: #testable provides access only for internal functions;
file-private and private declarations are not visible outside of their
usual scope when using #testable.

Mocking Objective-C code in Swift Unit-Tests

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.