Mock
Intro¶
Mocking is required when you want to just create some test objects and their dependency on the test object itself.
It goes hand in hand with Stubs and Mocking.
I do feel like I still haven't grasp its actual understanding yet but I'm trying to learn as much as possible around Dependency injection via mocking or stubing.
You can specify Mocking certain objects or classes / structs because testing can't be done on them individually as a unit.
One case was related to User Notifications
being not able to test in unit tests.
So if the Production class is defined like this.
// Implementation Production Classes
protocol CustomControllerType {
var viewModel: CustomViewModelProtocol
var rx: RxSwift.Reactive<Project.Custom.Controller> { .init(Custom.Controller() )}
func configure(for session: Sessionable, viewModelType: CustomViewModelProtocol.Type)
}
protocol CustomViewModelProtocol {
var Input init(context: Sessionable, customStorage: customMetadataStorageType, userDefaults: UserDefaults)
var input: CustomViewModel.Input { get }
var output: CustomViewModel.Output { get }
var customCoordinator: RxCustomCoordinatorProtocol { get }
var disposeBag: DisposeBag { get }
}
class ProductionProject.CustomViewModel : CustomViewModelProtocol {
public let input: Input
public let output: Output
public let customCoordinator: RxCustomCoordinatorProtocol
public required init(context: Sessionable,
customStorage: customMetadataStorageType,
userDefaults: UserDefaults = .standard) {
self.customCoordinator = customCoordinator().rx
let listSectionsDriver = Observable
.create{ }.asDriver(onErrorJustReturn: [])
self.input = Input(customTypeGlobalAction: PublishSubject<CustomActionType>().asObserver(),
refreshAction: PublishSubject<Void>().asObserver(),
shouldDisplaySomething: PublishSubject<Bool>.asObserver())
self.output = Output(
listSections: listSectionsDriver,
customState: PublishSubject<CustomSection>().asDriver(),
summaryMessage: .empty(),
confirmCancel: .empty() )
}
}
// MARK: • Input + Output
extension CustomViewModel {
public struct Input {
public let customTypeGlobalAction: AnyObserver<CustomActionType>
public let refreshAction: AnyObserver<Void>
public let shouldDisplaySomething: AnyObserver<Bool>
}
public struct Output {
public let listSections: Driver<[CustomSection]>
public let customState: Driver<CustomState>
public let summaryMessage: Driver<String>
public let confirmCancel: Signal<Void>
}
}
// Defining Namespace
public enum Custom { }
public extension Custom {
final class Controller: CustomControllerType {
var rx: Reactive<Custom.Controller> { .init(self) }
private(set) var viewModel: CustomViewModelProtocol
init() {
self.viewModel = CustomViewModel(context: Sessionable())
}
func configure(for session: Sessionable, viewModelType: CustomViewModelProtocol.Type) {
}
}
}
Mock extension for mocking classes.
import RxSwift
import CustomViewModel
extension Mock {
class CustomController: CustomControllerType {
var viewModel: ProjectViewModel.CustomViewModelProtocol = Mock
.CustomViewModel(
context: SessionContext(),
customStorage: Custom.Metadata.Storage(),
userDefaults: .standard)
var rx: RxSwift.Reactive<ProductionProject.Custom.Controller> { .init(Custom.Controller()) }
func configure(session: ProductionProject.Sessionable,
viewModelType: ProductionProject.CustomViewModelProtocol.Type) { }
}
class CustomViewModel: CustomViewModelProtocol {
var input: ProductionProject.CustomViewModel.Input
var output: ProductionProject.CustomViewModel.Output
init(context: ProductionProject.Sessionable,
customStorage: customMetadataStorageType,
userDefaults: UserDefaults) {
input = ProductionProject
.CustomViewModel
.Input(customTypeGlobalAction: .empty(),
refreshAction: .empty(),
shouldDisplaySomething: .empty())
output = ProductionProject
.CustomViewModel
.Output(listSections: .empty(),
customState: .empty(),
summaryMessage: .empty(),
confirmCancel: .empty())
}
}
}