Mam klasy, który chcę przetestować za pomocą XCTest, a ta klasa wygląda mniej więcej tak:Mocking Singleton/sharedInstance w Swift
public class MyClass: NSObject {
func method() {
// Do something...
// ...
SingletonClass.sharedInstance.callMethod()
}
}
Klasa używa pojedyncza który jest zaimplementowany jako to:
public class SingletonClass: NSObject {
// Only accessible using singleton
static let sharedInstance = SingletonClass()
private override init() {
super.init()
}
func callMethod() {
// Do something crazy that shouldn't run under tests
}
}
Teraz do testu. Chcę przetestować, że method()
faktycznie robi to, co powinien zrobić, ale nie chcę wywoływać kodu w callMethod()
(ponieważ robi straszne rzeczy async/network/thread, które nie powinny działać w testach MyClass, i spowoduje awarię testów).
Więc co ja w zasadzie chciałoby zrobić to w ten sposób:
SingletonClass = MockSingletonClass: SingletonClass {
override func callMethod() {
// Do nothing
}
let myObject = MyClass()
myObject.method()
// Check if tests passed
To oczywiście nie jest ważny Swift, ale masz pomysł. Jak mogę przesłonić callMethod()
tylko dla tego konkretnego testu, aby uczynić go nieszkodliwym?
EDYCJA: Próbowałem rozwiązać to za pomocą zastrzyku zależności, ale napotkał duże problemy. I stworzył własny startowe metody po prostu być wykorzystywane do badań, takich, że mogę tworzyć swoje obiekty tak:
let myObject = MyClass(singleton: MockSingletonClass)
i niech MojaKlasa wyglądać następująco
public class MyClass: NSObject {
let singleton: SingletonClass
init(mockSingleton: SingletonClass){
self.singleton = mockSingleton
}
init() {
singleton = SingletonClass.sharedInstance
}
func method() {
// Do something...
// ...
singleton.callMethod()
}
}
Mieszanie w kodzie testowym z resztą kodu jest coś, co wydaje mi się nieco nieprzyjemne, ale w porządku. Dużym problemem było to, że miałem dwa singletons skonstruowany tak w moim projekcie, zarówno przedstawieniu siebie:
public class FirstSingletonClass: NSObject {
// Only accessible using singleton
static let sharedInstance = FirstSingletonClass()
let secondSingleton: SecondSingletonClass
init(mockSingleton: SecondSingletonClass){
self.secondSingleton = mockSingleton
}
private override init() {
secondSingleton = SecondSingletonClass.sharedInstance
super.init()
}
func someMethod(){
// Use secondSingleton
}
}
public class SecondSingletonClass: NSObject {
// Only accessible using singleton
static let sharedInstance = SecondSingletonClass()
let firstSingleton: FirstSingletonClass
init(mockSingleton: FirstSingletonClass){
self.firstSingleton = mockSingleton
}
private override init() {
firstSingleton = FirstSingletonClass.sharedInstance
super.init()
}
func someOtherMethod(){
// Use firstSingleton
}
}
To stworzyło impasu, kiedy jeden z pojedynczych gdzie pierwszy użyty, jako metodę init, by czekać na init metoda drugiej, i tak dalej ...
Okropne rozwiązanie pozwalające na wyciek testów do kodu produkcyjnego – IlyaEremin