Swift unit tests mock class static method - ios

I have a static method in class
class A {
static func myStaticMethod() -> B {
return B()
}
}
class B {
func getTitle() -> String {
// some functionality here
}
}
In my class method that i want to test i use it like:
func someBoolFunc() -> Bool {
var b = A.myStaticMethod()
if (b.getTitle() = “hello”) {
return true
}
return false
}
How to write mock class for this... I tried:
class MockA: A {
var myTitle:String
// this seems incorrect, because i didn't override static function
func myStaticMethod() -> MockB {
var b = MockB()
b.title = myTitle
return b
}
}
class MockB: B {
var myTitle:String
func getTitle() -> String {
return myTitle
}
}
And in tests i wanted to use something like:
func testExample() {
A = MockA
MockA.title = "My title"
// And now this func should use my MockA instead of A
someBoolFunc()
}
But of course it is only in theory :(

Maybe this way?
protocol AProto {
static func myStaticMethod() -> BProto
}
class A: AProto {
static func myStaticMethod() -> BProto {
return B()
}
}
class MockA: AProto {
static func myStaticMethod() -> BProto {
return MockB()
}
}
protocol BProto {
func getTitle() -> String
}
class B: BProto {
func getTitle() -> String {
return "hello"
}
}
class MockB: BProto {
func getTitle() -> String {
return "bye bye"
}
}
func someBoolFunc(_ aProto: AProto.Type = A.self) -> Bool {
var b = aProto.myStaticMethod()
if (b.getTitle() == "hello") {
return true
}
return false
}
print(someBoolFunc())
print(someBoolFunc(MockA.self))

Related

How to mock final class and static func for unit test with Swift?

I have a class with static function like this.
UserAuthentication.swift
final class UserAuthentication {
/// Check is logged in
/// - Returns: boolean true is login, false not login
#objc public static func isLogin() -> Bool {
return true
}
}
I want to write a unit test for checkTermAndCondition function which call it in my HomeInteractor class
HomeInteractor.swift
class HomeInteractor: HomeInteractorBusinessLogic {
var presenter: HomePresenterInterface!
var worker: HomeWorker = HomeWorker(with: HomeService())
func checkTermAndCondition() {
if UserAuthentication.isLogin() {
///do true case
} else {
///do fasle case
}
}
}
Have anyone ever done it ?
Appreciate any help from you.
You cannot mock a static method on a final class.
You should either change the method to class and make the class non-final or even better, inject UserAuthentication as a protocol to HomeInteractor and in your unit tests, inject a mock type rather than your real production type.
protocol UserAuthenticator {
/// Check is logged in
/// - Returns: boolean true is login, false not login
static func isLogin() -> Bool
}
final class UserAuthentication: UserAuthenticator {
#objc public static func isLogin() -> Bool {
return true
}
}
final class UserAuthenticationMock: UserAuthenticator {
static var shouldLogin: Bool = false
static func isLogin() -> Bool {
shouldLogin
}
}
class HomeInteractor: HomeInteractorBusinessLogic {
var presenter: HomePresenterInterface!
var worker: HomeWorker = HomeWorker(with: HomeService())
let userAuthenticator: UserAuthenticator.Type
init(userAuthenticator: UserAuthenticator.Type) {
self.userAuthenticator = userAuthenticator
}
func checkTermAndCondition() {
if userAuthenticator.isLogin() {
} else {
}
}
}
Inject UserAuthentication.self for your prod code, while UserAuthenticationMock.self for the tests.
let prodHomeInteractor = HomeInteractor(userAuthenticator: UserAuthentication.self)
let testHomeInteractor = HomeInteractor(userAuthenticator: UserAuthenticationMock.self)

Swift Class as Parameter to Instantiate Later

I have the following Swift code. My goal is to be able to pass in the type or class of either FirstItem or SecondItem into the Manager to be able to instantiate later on (by calling the createItem function).
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem)
How can I fix the code above to allow for this so that the manager accepts the class itself instead of an instance of the class.
By changing the parameter type that you are accepting in the init function to CustomItem.Type and changing the value you are passing in to FirstItem.self this works. For example changing the code to something like the following should work.
protocol CustomItem {
static func instantiate() -> CustomItem
}
class FirstItem: CustomItem {
static func instantiate() -> CustomItem {
return FirstItem()
}
}
class SecondItem: CustomItem {
static func instantiate() -> CustomItem {
return SecondItem()
}
}
class Manager {
var itemClass: CustomItem.Type
func createItem() {
let itemInstance = itemClass.instantiate()
}
init(itemClass: CustomItem.Type) {
self.itemClass = itemClass
}
}
let manager = Manager(itemClass: FirstItem.self)

Swift restrict generic type to Type

How can I restrict a generic type to be a type, not an instance of a type?
If I have a class:
class SomeClass<T: SomeProtocol> {}
how can I ensure that T is only an instance of AnyClass (which is just AnyObject.Type)
My protocol only has static methods and in order to call those methods I have to do instance.dynamicType.protocolMethod whereas I want to do someType.protocolMethod
AFAIK, Swift does not allow you to use a metatype as a generic type. (I believe this is along the lines of what Sam Giddins wished for in Swift 3.)
You can, however, use it as a value. Instead of making T a type parameter, make it a property:
protocol SomeProtocol {
static func foo()
}
struct Concrete: SomeProtocol {
static func foo() {
print("I am concrete")
}
}
class SomeClass {
let T: SomeProtocol.Type
init(T: SomeProtocol.Type) {
self.T = T
}
func go() {
T.foo() // no need for dynamicType
}
}
SomeClass(T: Concrete.self).go()
If, as you say, your protocol contains only static methods, then this is sufficient. However, if you need to tie a generic parameter to the type, that’s possible too:
class SomeClass<U: SomeProtocol> {
let T: U.Type
init(T: U.Type) {
self.T = T
}
func go(value: U) {
T.foo()
}
}
SomeClass(T: Concrete.self).go(Concrete())
protocol P {
static func foo()
}
class A : P {
static func foo() {
print("A class")
}
}
class B : P {
static func foo() {
print("C class")
}
}
var type1 = A.self
var type2 = B.self
func check(cls: AnyClass)->Void {
switch cls {
case is A.Type:
A.foo()
case is B.Type:
B.foo()
default:
break
}
}
check(type1) // A class
check(type2) // C class
let i = Int.self // Int.Type
let ao = AnyObject.self // AnyObject.Protocol
let ac = AnyClass.self // AnyClass.Protocol
let p = P.self // P.Protocol
let f = check.self // (AnyClass)->Void
Edit
protocol P {
static func f()
}
class A : P {
static func f() {
print("A")
}
}
class B : P {
static func f() {
print("B")
}
}
func f(cls: P.Type) {
cls.f()
}
f(A) // A
f(B) // B
class Test<T: P> {
func foo() {
T.f()
}
}
Test<A>().foo() // A
Test<B>().foo() // B

Swift generic function parameter as Class

I'm not sure that this not duplicate. I have protocol and several classs that confurm to it.
protocol DbObject: class {
class func tableName() -> String
init(set: FMResultSet)
func save(db: FMDatabase)
}
And here how I use it:
func updateTable<T where T:DbObject>(nodes: [T]) {
self.db.executeUpdate("DELETE from \(T.tableName())")
for elem in nodes {
elem.save(self.db)
}
}
func loadAllFromObjectTable<T where T:DbObject>(cl: T) -> [T] {
var objArr = [T]()
if let set = db.executeQuery("select * from \(T.tableName())") {
while (set.next() ?? false) {
let obj = T(set: set)
objArr.append(obj)
}
}
return objArr
}
But I wanna that function loadAllFromObjectTable get as parameter class that confirms protocol, not object. How can I achieve that?
EDIT:
From here https://stackoverflow.com/a/26229630/820795
func loadAllFromObjectTable<T where T:DbObject>(_: T.Type) -> [T] {
var objArr = [T]()
if let set = db.executeQuery("select from \(T.tableName())") {
while (set.next() ?? false) {
if let obj = T(set: set)
objArr.append(obj)
}
}
return objArr
}
And usage:
manager.loadAllFromObjectTable(SignNote.self)

Generic class inheritance in Swift

I have the following class:
class BaseCache<T: Equatable>: NSObject {
var allEntities = [T]()
// MARK: - Append
func appendEntities(newEntities: [T]) {
....
}
}
Now I want to subclass it, but I get annoying error, that my type "does not conform to protocol 'Equatable'":
It seems generics in Swift are real pain-in-the-ass.
Your class definition of TrackingCache is wrong. It repeats the generic parameter:
class TrackingCache<AftershipTracking>: BaseCache<AftershipTracking> { }
It should be left out:
class TrackingCache: BaseCache<AftershipTracking> { }
This triggers the underlying swift error Classes derived from generic classes must also be generic. You can work around this issue by specifying a type parameter that is required to be or inherit from AftershipTracking:
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> { }
Full example:
class BaseCache<T: Equatable>: NSObject {
var items: [T] = []
func appendItems( items: [T]) {
self.items += items
didAppendItems()
}
func didAppendItems() {} // for overriding
}
class AftershipTracking: NSObject {
var identifier: Int
init( identifier: Int) {
self.identifier = identifier
super.init()
}
}
extension AftershipTracking: Equatable { }
func ==( lhs: AftershipTracking, rhs: AftershipTracking) -> Bool {
return lhs.identifier == rhs.identifier
}
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> {
override func didAppendItems() {
// do something
}
}
let a = TrackingCache<AftershipTracking>()
let b = TrackingCache<AftershipTracking>()
a.appendItems( [AftershipTracking( identifier: 1)])
b.appendItems( [AftershipTracking( identifier: 1)])
let result = a.items == b.items // true
this should work: < swift 4 >
class TrackingCache<T: AftershipTracking>: BaseCache<T>
Another example:
protocol P {
}
class C: P {
}
class CS: C {
}
class L<T:P> {
let c: T
init(_ c: T) {
self.c = c
}
}
class LS<T:CS>:L<T> {
}
let i = LS(CS())
i.c
c is CS now.

Resources