Быстрые одиночные тесты
Я пытаюсь научиться тестировать с Quick и Nimble, и я написал простой синглтон и некоторые тесты
Синглтон:
public class LocationManager : NSObject {
// Singleton
public class var sharedInstance : LocationManager {
struct Static {
static var instance : LocationManager?
static var token : dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = LocationManager()
}
return Static.instance!
}
}
Мои тесты:
class LocationManagerSpec: QuickSpec {
override func spec() {
describe("Location manager") {
let locationManager = LocationManager.sharedInstance
context("initialized") {
it("is not nil") {
expect(locationManager).notTo(beNil())
}
it("is unique") {
let tempLocationManager = LocationManager()
expect(locationManager).notTo(equal(tempLocationManager))
}
it("and shared instances are same") {
let tempLocationManager = LocationManager.sharedInstance
expect(locationManager).to(equal(tempLocationManager))
}
}
}
}
}
Как я могу проверить, что мой синглтон является потокобезопасным?
1 ответ
Чтобы проверить, что ваш синглтон безопасен для потоков, я сделал небольшой пример. Вы можете найти весь код в этом репозитории Github в основном:
- создавая кучу потоков, просящих синглтон
- хранение каждого возвращаемого объекта в массиве
- проверка всех объектов, содержащихся в этом массиве, одинаковы
Я обнаружил несколько проблем (используя Xcode 6.2 ergo Swift 1.1), даже сбои компилятора. Итак, после этого принятого SO ответа я изменил вашу реализацию Singleton, избегая классического использования GCD. Этими указателями и небезопасными изменяемыми указателями вы должны управлять, и из-за этого у меня возникали сбои
Итак, ваш синглтон-класс сейчас:
import Foundation
public class LocationManager: NSObject {
public class var sharedInstance: LocationManager {
struct Static {
static let instance: LocationManager = LocationManager()
}
return Static.instance
}
}
И чтобы проверить это, вам нужно дождаться окончания всех потоков. Поскольку тестовый прогон работает на MainThread, вам нужно использовать ожидание
describe("Accesing Location manager from multiple concurrent threads") {
context("When created") {
it("should return always the same object for every of these 500 threads") {
var allSingletons = Array<LocationManager>()
for i in 1...10 {
println("Launching thread \(i)")
dispatch_async(self.globalBackgroundQueue) {
allSingletons.append(LocationManager.sharedInstance)
}
}
expect(self.allSingletonsEqual(inArray: allSingletons, singleton: LocationManager.sharedInstance)).toEventually(beTrue(), timeout: 10)
}
}
}
Важной частью является эта:
expect(self.allSingletonsEqual(inArray: allSingletons, singleton: LocationManager.sharedInstance)).toEventually(beTrue(), timeout: 10)
Здесь я просто вызываю функцию, чтобы убедиться, что каждый сохраненный объект совпадает с переданным синглтоном, и даю 10-секундный тайм-аут
Весь тестовый класс:
import Quick
import Nimble
import QuickNimbleExample
class LocationManagerSpec: QuickSpec {
var globalBackgroundQueue: dispatch_queue_t {
return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
}
func allSingletonsEqual(#inArray: Array<LocationManager>, singleton: LocationManager) -> Bool {
for loc in inArray {
if loc != singleton {
return false
}
}
return true
}
override func spec() {
describe("Accesing Location manager from multiple concurrent threads") {
context("When created") {
it("should return always the same object for every of these 500 threads") {
var allSingletons = Array<LocationManager>()
for i in 1...10 {
println("Launching thread \(i)")
dispatch_async(self.globalBackgroundQueue) {
allSingletons.append(LocationManager.sharedInstance)
}
}
expect(self.allSingletonsEqual(inArray: allSingletons, singleton: LocationManager.sharedInstance)).toEventually(beTrue(), timeout: 10)
}
}
}
}
}