Skip to content

Instantly share code, notes, and snippets.

@yosshi4486
Last active April 11, 2021 00:42
Show Gist options
  • Save yosshi4486/6cea0bef05aae8f22fd6edc1f3e91b94 to your computer and use it in GitHub Desktop.
Save yosshi4486/6cea0bef05aae8f22fd6edc1f3e91b94 to your computer and use it in GitHub Desktop.
テストターゲットとメインターゲットの両方から読み込んでテスト環境値の注入を行うフレームワークのファイルサンプル
// .gitignoreで該当のtestEnvironmentBackingStore.jsonファイルだけ取り除いておくようにする。
// ProcessInfoを使わずに値をメインターゲットに渡したかったので考えてみた。各種設定項目をいじることで流用可能。
// これをProcessInfoの代わりに、TestEnvironment.sharedとして値を取り出すようにすれば、ちょっとマシになるかなと。
// フレームワーク自体にアプリケーションのテストに関する知識が書き込まれているので、OSS化は難しい?
// 参考にしたのはSKTestSessionの設計
//
// TestEnvironment.swift
// BulistsTestEnvironment
//
// Created by yosshi4486 on 2021/04/10.
//
import Foundation
#if TEST
/// シミュレートしたいエラーを列挙する
public enum TestEnvironmentErrorCode: Int, Codable {
case `default`
public var error: Error {
switch self {
case .default:
return NSError(domain: "UITest", code: 1, userInfo: [
NSLocalizedDescriptionKey: "UI Test Error",
NSLocalizedFailureReasonErrorKey: "An Error is occured from UITest.",
NSLocalizedRecoverySuggestionErrorKey: "Nothing.",
])
}
}
}
public struct TestSettings: Codable {
/// NSManagedObjectContextの保存を実行した際にエラーを発生させるかどうかのBool値. デフォルトはfalse
public var saveErrorEnabled: Bool = false
/// `saveErrorEnabled`によって起こった保存エラーで投げるエラーの種類, デフォルトはUIエラー
public var saveErrorCode: TestEnvironmentErrorCode = .default
/// NSPersistentContainerの読み込みを実行した際にエラーを発生させるかどうかのBool値. デフォルトはfalse
public var loadErrorEnabled: Bool = false
/// `loadErrorEnabled`によって起こった読み込みエラーで投げるエラーの種類。デフォルトはUIエラー
public var loadErrorCode: TestEnvironmentErrorCode = .default
/// `XCUIApplication().launch`の際にローカルストレージのデータを削除するかどうかのBool値. デフォルトはfalse.
public var clearPersistedDataOnLaunch: Bool = false
/// `XCUIApplication().launch`の際に初期データを何件投入するかの整数. デフォルトは0件
public var numberOfAddDataOnLaunch: Int = 0
}
/// テスト設定をテストターゲットから操作して、アプリケーションターゲットで読み込ませるための環境
@dynamicMemberLookup public final class TestEnvironment {
/// テスト環境は1つしか存在しない
public static let shared = TestEnvironment()
public subscript<U>(dynamicMember keyPath: WritableKeyPath<TestSettings, U>) -> U {
get {
loadFromBackingStore()
return settings[keyPath:keyPath]
}
set {
settings[keyPath:keyPath] = newValue
saveToBackingStore()
}
}
/// TestSettingsをjsonにエンコードするエンコーダー
private let jsonEncoder = JSONEncoder()
/// 保存されたjsonをTestSettingsにデコードするデコーダー
private let jsonDecoder = JSONDecoder()
/// 設定用のファイルを保存/読み出しするパス
///
/// gitignoreでこのパスをignoreしており差分は出ないようになっている。あくまでソースディレクトリ内に追加でファイルを作成して操作しているため
/// CIでもコケることはない
let backingStoreJSONFilePath = URL(fileURLWithPath: #file)
.deletingLastPathComponent()
.appendingPathComponent("testEnvironmentBackingStore.json")
/// 読み書きする設定のキャッシュ
private var settings: TestSettings = .init()
private init() {
loadFromBackingStore()
}
deinit {
resetToBaseStates()
saveToBackingStore()
}
/// 設定した変数を初期値にリセットする.
public func resetToBaseStates() {
settings = .init()
saveToBackingStore()
}
private func loadFromBackingStore() {
if let data = try? Data(contentsOf: backingStoreJSONFilePath), let settings = try? jsonDecoder.decode(TestSettings.self, from: data) {
self.settings = settings
} else {
// 存在しない場合は初期データを投入する。
saveToBackingStore()
}
}
private func saveToBackingStore() {
let encoded = try! jsonEncoder.encode(settings)
try! encoded.write(to: backingStoreJSONFilePath, options: .atomicWrite)
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment