diff --git a/Viperit.xcodeproj/project.pbxproj b/Viperit.xcodeproj/project.pbxproj index 4bf440f..9ec8684 100644 --- a/Viperit.xcodeproj/project.pbxproj +++ b/Viperit.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 53388C291DD9FFDF00EA4CEC /* Viperit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */; }; 53388C2A1DD9FFDF00EA4CEC /* Viperit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5367C0CF1DD7C68D005B6676 /* Viperit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 5360ADF31E81280100C1AACB /* PresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5360ADF21E81280100C1AACB /* PresenterTests.swift */; }; 5367C0DC1DD7C69E005B6676 /* ViperitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C641DD50DDF00088AAC /* ViperitError.swift */; }; 5367C0DD1DD7C69E005B6676 /* ViperComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C631DD50DDF00088AAC /* ViperComponent.swift */; }; 5367C0DE1DD7C69E005B6676 /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53532C5F1DD50DDF00088AAC /* Module.swift */; }; @@ -91,6 +92,7 @@ 53532C621DD50DDF00088AAC /* UserInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInterface.swift; sourceTree = ""; }; 53532C631DD50DDF00088AAC /* ViperComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViperComponent.swift; sourceTree = ""; }; 53532C641DD50DDF00088AAC /* ViperitError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViperitError.swift; sourceTree = ""; }; + 5360ADF21E81280100C1AACB /* PresenterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenterTests.swift; sourceTree = ""; }; 5367C0CF1DD7C68D005B6676 /* Viperit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Viperit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5368EDC61E7FF91F0028D8CA /* RouterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; 5368EDC81E7FF9A40028D8CA /* TestModules.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestModules.swift; sourceTree = ""; }; @@ -212,6 +214,7 @@ children = ( 534AF1EF1E7BED06009D2D61 /* Sample */, 537D19E31E7BE9B100A758FF /* ModuleTests.swift */, + 5360ADF21E81280100C1AACB /* PresenterTests.swift */, 5368EDC61E7FF91F0028D8CA /* RouterTests.swift */, 53532C521DD50BD200088AAC /* Info.plist */, ); @@ -500,6 +503,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5360ADF31E81280100C1AACB /* PresenterTests.swift in Sources */, 5368EDD61E801EDB0028D8CA /* SampleView.swift in Sources */, 5368EDD21E801EDB0028D8CA /* SampleDisplayData.swift in Sources */, 5368EDC91E7FF9A40028D8CA /* TestModules.swift in Sources */, diff --git a/Viperit/Core/Module.swift b/Viperit/Core/Module.swift index 33044e0..4b77fa1 100644 --- a/Viperit/Core/Module.swift +++ b/Viperit/Core/Module.swift @@ -48,7 +48,7 @@ public struct Module { } } -//MARK: - Inject Mock View for Testing +//MARK: - Inject Mock Components for Testing public extension Module { public mutating func injectMock(view mockView: UserInterface) { @@ -57,6 +57,28 @@ public extension Module { view._displayData = displayData presenter._view = view } + + public mutating func injectMock(interactor mockInteractor: Interactor) { + interactor = mockInteractor + interactor._presenter = presenter + presenter._interactor = interactor + } + + public mutating func injectMock(presenter mockPresenter: Presenter) { + presenter = mockPresenter + presenter._view = view + presenter._interactor = interactor + presenter._router = router + view._presenter = presenter + interactor._presenter = presenter + router._presenter = presenter + } + + public mutating func injectMock(router mockRouter: Router) { + router = mockRouter + router._presenter = presenter + presenter._router = router + } } diff --git a/Viperit/Core/Router.swift b/Viperit/Core/Router.swift index 4b76b96..5f37f29 100644 --- a/Viperit/Core/Router.swift +++ b/Viperit/Core/Router.swift @@ -26,7 +26,9 @@ open class Router { } open func show(from: UIViewController, setupData: Any, embedInNavController: Bool = false) { - print(ViperitError.methodNotImplemented.description) + let view = embedInNavController ? embedInNavigationController() : _view + _presenter.setupView(data: setupData) + from.show(view, sender: nil) } required public init() { } diff --git a/ViperitTests/PresenterTests.swift b/ViperitTests/PresenterTests.swift new file mode 100644 index 0000000..8523508 --- /dev/null +++ b/ViperitTests/PresenterTests.swift @@ -0,0 +1,118 @@ +// +// PresenterTests.swift +// Viperit +// +// Created by Ferran Abelló on 21/03/2017. +// Copyright © 2017 Ferran Abelló. All rights reserved. +// + +import XCTest +import Viperit + +//MARK: - Timeout +private let kTimeout: TimeInterval = 0.5 + +//MARK: - View Lifecycle +private let kViewHasLoaded = "viewHasLoaded" +private let kViewIsAboutToAppear = "viewIsAboutToAppear" +private let kViewHasAppeared = "viewHasAppeared" +private let kViewIsAboutToDisappear = "viewIsAboutToDisappear" +private let kViewHasDisappeared = "viewHasDisappeared" + +//MARK: - Setup methods +private let kSetupView = "setupView" + +//MARK: - Mock Presenter +private class MockPresenter: Presenter, SamplePresenterInterface { + var expectation: XCTestExpectation! + var methodExpected: String! + + private func assert(method: String) { + if methodExpected == method { + XCTAssert(true) + expectation.fulfill() + } + } + + override func viewHasLoaded() { + assert(method: kViewHasLoaded) + } + + override func viewIsAboutToAppear() { + assert(method: kViewIsAboutToAppear) + } + + override func viewHasAppeared() { + assert(method: kViewHasAppeared) + } + + override func viewIsAboutToDisappear() { + assert(method: kViewIsAboutToDisappear) + } + + override func viewHasDisappeared() { + assert(method: kViewHasDisappeared) + } + + override func setupView(data: Any) { + assert(method: kSetupView) + } +} + +//MARK: - Presenter Tests +class PresenterTests: XCTestCase { + private func createTestModuleWithMockPresenter(methodToTest: String) -> Module { + var module = Module.build(TestModules.sample, bundle: Bundle(for: SampleRouter.self)) + let mockPresenter = MockPresenter() + mockPresenter.expectation = expectation(description: "Expecting method: \(methodToTest)") + mockPresenter.methodExpected = methodToTest + module.injectMock(presenter: mockPresenter) + return module + } + + private func expectViewLifecycle(method: String) { + let module = createTestModuleWithMockPresenter(methodToTest: method) + + //Simulate view lifecycle + _ = module.view.view + module.view.viewWillAppear(false) + module.view.viewDidAppear(false) + module.view.viewWillDisappear(false) + module.view.viewDidDisappear(false) + + expect(timeout: kTimeout, errorMessage: "\(method) timed out (> \(kTimeout)s") + } + + + func testViewHasLoaded() { + expectViewLifecycle(method: kViewHasLoaded) + } + + func testViewIsAboutToAppear() { + expectViewLifecycle(method: kViewIsAboutToAppear) + } + + func testViewHasAppeared() { + expectViewLifecycle(method: kViewHasAppeared) + } + + func testSetupView() { + let mockView = UIViewController() + _ = mockView.view + let module = createTestModuleWithMockPresenter(methodToTest: kSetupView) + + //Simulate show module with router function using setup data + module.router.show(from: mockView, setupData: "randomData") + expect(timeout: kTimeout, errorMessage: "\(kSetupView) timed out (> \(kTimeout)s") + } + + + func expect(timeout: TimeInterval, errorMessage: String) { + waitForExpectations(timeout: timeout) { error in + guard error != nil else { return } + XCTFail(errorMessage) + } + } + + +} diff --git a/ViperitTests/Sample/SamplePresenter.swift b/ViperitTests/Sample/SamplePresenter.swift index f76469d..5f1464c 100644 --- a/ViperitTests/Sample/SamplePresenter.swift +++ b/ViperitTests/Sample/SamplePresenter.swift @@ -9,9 +9,17 @@ import Foundation import Viperit +//Public API for Presenter (these methods will be visible from the View) +protocol SamplePresenterInterface { +} + final class SamplePresenter: Presenter { } +extension SamplePresenter: SamplePresenterInterface { + +} + // MARK: - VIPER COMPONENTS API (Auto-generated code) private extension SamplePresenter { diff --git a/ViperitTests/Sample/SampleView.swift b/ViperitTests/Sample/SampleView.swift index d654991..8e15d69 100644 --- a/ViperitTests/Sample/SampleView.swift +++ b/ViperitTests/Sample/SampleView.swift @@ -23,8 +23,8 @@ extension SampleView: SampleViewInterface { // MARK: - VIPER COMPONENTS API (Auto-generated code) private extension SampleView { - var presenter: SamplePresenter { - return _presenter as! SamplePresenter + var presenter: SamplePresenterInterface { + return _presenter as! SamplePresenterInterface } var displayData: SampleDisplayData { return _displayData as! SampleDisplayData