I would like to use nested classes in Swift for mocking purposes. Imagine bar() method of following Foo class is under the test:
class Foo: NSObject {
func baz() -> String {
return "baz"
}
func bar() -> String {
return self.baz().stringByReplacingOccurrencesOfString("z", withString: "r")
}
}
I want to mock baz() method and verify that bar() depends on it with following test:
class FooTests: XCTestCase {
func testBar() {
class FooMock: Foo {
override func baz() -> String {
return "zzz"
}
}
let mock = FooMock()
let result = mock.bar()
XCTAssertEqual("rrr", result)
}
}
I have Foo class added to both application and test targets to bypass access modifiers issue with test target.
The application fails to compile with below error:
Global is external, but doesn't have external or weak linkage!
%4* (%0*, i8*)* #_TToFCFC13TestTestTests8FooTests7testBarFS0_FT_T_L_7FooMock3bazfS1_FT_SS
invalid linkage type for function declaration
%4* (%0*, i8*)* #_TToFCFC13TestTestTests8FooTests7testBarFS0_FT_T_L_7FooMock3bazfS1_FT_SS
LLVM ERROR: Broken module found, compilation aborted!
After moving FooMock to separate source file it compiles and works just fine.
When using nested class to mock any methods from iOS standard frameworks it works just fine:
func testMockDefaults() {
class NSUserDefaultsMock: NSUserDefaults {
override func objectForKey(defaultName: String) -> AnyObject? {
return "Super secret!"
}
}
let defaults = NSUserDefaultsMock(suiteName: "SuiteName")!
defaults.setObject("Test", forKey: "Test")
let result = defaults.objectForKey("Test") as String
XCTAssertEqual("Super secret!", result)
}
Could you please explain me what am I missing and what is the reason behind this?
Related
I have tried following Cannot explicitly specialize a generic function and I can't get a hold of this
This is my code
class A<T>{
func test(){
self.verify(value : T.self)
}
func verify<T>(value: T) {
print("Default")
}
}
extension A where T == String {
func verify<T>(value: T){
print("Str")
}
}
let o = A<String>()
o.test()
The test is the only available function that can be called. When I'm executing this, I get
Default not Str.
But according to the generics, I should get Str. What am I doing wrong here? What should I do to keep extension A give me Str if T == String
Ok, so I'm learning generics new here. And this is what I got,
class A<T>{
func test(){
print("Def Test")
verifyVal()
}
func verify() {
print("Default verifyVal")
}
}
extension A where T == String {
func test(){
print("String")
}
}
extension A where T == Int {
func test(){
print("Int")
}
}
let o = A<String>()
o.test()
let a = A<Int>()
a.test()
This will give you the expected result
String
Int
So basically overload the test method instead of verify.
However, there are some caveats
If the method test is inherited from another class , say A is a child of N where the method test was originally written, there is no way you can override it in the extensions.
Thats it.
I am trying to unit test simple HttpClient behaviour. For that I have created an GenericHttpClientInterface protocol and concrete class GenericHttpClient that implements this protocol.
protocol GenericHttpClientInterface {
func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T>
}
class GenericHttpClient: GenericHttpClientInterface {
func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T> {
return URLSession.shared.rx.data(request: request).jsonDecode(to: T.self)
}
}
What I wanted to achieve is to mock that class :
class MockHttpClient: GenericHttpClientInterface {
var invokedMakeRequestCount = 0
var invokedMakeRequestParameters: (request: URLRequest, Void)?
var stubbedMakeRequestResult: Observable<Any>!
func makeRequest<T: Decodable>(request: URLRequest) -> Observable<T> {
invokedMakeRequestCount += 1
invokedMakeRequestParameters = (request, ())
return stubbedMakeRequestResult as! Observable<T>;
}
}
What gives ma a problem is that the method I am mocking has generic parameter T inside which is a class that the request will be decoded to. I don't know this parameter until I call this function so basically in MockHttpClient class for a property that stores stub data for makeRequest I've created:
stubbedMakeRequestResult: Observable<Any>
and after returning it I'm trying to cast that to result type Observable. This gives me an warning
Cast from 'Observable<Any>?' to unrelated type 'Observable<T>' always fails
and in consequence
Thread 1: signal SIGABRT.
Any idea how to stub that data?
Example test that creates SIGABRT:
class GenericHttpTest: XCTestCase {
var sut: Repository!
var mockHttpClient: MockHttpClient!
override func setUp() {
mockHttpClient = MockHttpClient()
sut = Repository(httpClient: mockHttpClient)
}
let test_mocked_data_stub = DataModelStruct(args: DataModelStruct.InsideModelStruct(foo1: "bar"))
func test_should_return_mocked_data_from_mock_http_client() {
mockHttpClient.stubbedMakeRequestResult = Observable.just(test_mocked_data_stub)
let response = try! sut.getFooBar().toBlocking().first()
XCTAssertEqual(response, test_mocked_data_stub)
}
}
Due to the fact that generics are invariant in Swift, Observable<Any> will never ever be convertible to Observable<T>, unless T is Any. This is what cause your crash, as when you assign the value to stubbedMakeRequestResult, the concrete Observable is converted to Observable<Any>, and there's no turning point from here.
What you can do to avoid this is to make stubbedMakeRequestResult an Any, as this will not make any conversions behind the scenes. A small problem is that you loose the type safety and inference, but you can fix this stubbing via a function:
class MockHttpClient: GenericHttpClientInterface {
...
var stubbedMakeRequestResult: Any!
func stubMakeRequestResult<T>(_ result: Observable<T>) {
stubbedMakeRequestResult = result
}
...
}
I'm trying to create a simple framework that has a function that returns "hello name" name being a passed argument. Below is the framework and code trying to call it.
Framework:
public class Greeter {
public init () {}
public static func greet(name: String) -> String {
return "Hello /(name)."
}
}
Code:
import Greeter
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let greetingString: String = Greeter.greet("Bob")
print(greetingString)
}
}
When I try typing out "greet("Bob")", what autocompletes is "(name: String) -> String greet(self: Greeter)". And when I manually type "greet("Bob")", I get the error: Instance member 'greet' cannot be used on type 'Greeter'; did you mean to use a value of this type instead?
You need to create an instance of Greeter class first and then call it's method.
let greeter = Greeter()
let greetingString: String = greeter.greet(name: "Bob")
print(greetingString)
Update: You don't need : String it's redundant here. So you can modify that line to:
let greetingString = greeter.greet(name: "Bob")
Consider the following code:
protocol Foo {
func f() -> Void
}
class Bar1: Foo {
func f() {
print("Bar1 f")
}
}
class Bar2: Foo {
func f() {
print("Bar2 f")
}
}
func function<T:Foo>(arg:T = Bar2()) {
arg.f()
}
It gives an error Value of type Bar2 cannot be converted to type T, which seems to be pure nonsense, because T is guaranteed to be compatible with Foo and this is the context within which the assignment should operate.
To prove it:
let global: Foo = Bar2()
global.f()
This works just fine.
I am wondering why such a discrepancy exists and if there is any workaround for it?
Cast the Bar2() as Generic type T
func function<T:Foo>(arg:T = Bar2() as! T) {
arg.f()
}
I know it is possible to pass class type to a function in swift:
func setGeneric<T>(type: T.Type){ }
setGeneric(Int.self)
But how we can return type from function? Writing something like
func getGeneric<T>() -> T.Type {
return Int.self
}
gives compiler error "Int is not identical to T". So is it possible to return type from a swift function?
Edit
Some explanation. I have classes that are used for persistence (I'm using Realm) and I have classes that acts as wrappers around this classes. All wrappers inherits from RealmClassWrapper which needs to know what Realm class it actually wraps. So lets say I have this realm model:
class RealmTodo: RLMObject {
dynamic var title = ""
}
and my wrappers supper class looks like this:
class RealmClassWrapper {
private let backingModel: RLMObject
//...
func backingModelType<T>() -> T.Type{ fatalError("must be implemented") }
}
and actual wrapper:
class Todo: RealmClassWrapper {
//some other properties
func backingModelType<T>() -> T.Type{ return RealmTodo.self }
}
You can return any type you want.
func getTypeOfInt() -> Int.Type { return Int.self }
func getTypeOfBool() -> Bool.Type { return Bool.self }
If the type is not determined from arguments or if the return is constant, there is no need to introduce a generic T type.
It works when I modify your function like this:
func getGeneric<T>(object: T) -> T.Type {
return T.self
}
getGeneric(0) // Swift.Int
You can force the downcast (as!) as below
func getGeneric<T>() -> T.Type {
return Int.self as! T.Type
}
But out of the function scope, you need to indicate the returned type:
var t:Int.Type = getGeneric()
Yes, this is possible. The problem here is that you say your function returns a generic T.type, but you always return Int.type. Since T is not always an Int, the compiler raises an error.
If you don't want to specify the return type you can use AnyClass as it instead of a template parameter.
class A {}
class B {}
public enum ExampleEnum: String {
case a
case b
func asClass() -> AnyClass {
switch self {
case .a:
return A.self
case .b:
return B.self
}
}
}
let myGoal : AnyClass = ExampleEnum.a.asClass()
You can also avoid the final cast to AnyClass, but compiler will show you an error