How to test controller class in MVC? - ios

I'm building a simple iOS app in Swift according to the Model-View-Controller pattern. I can test the Model class by giving it input data and asserting the output result against my expectation. But I was wondering how I could test the Controller class?
It seems that if I do want to test the Controller class, the testing logic would be much more complicated. Is there a standard way of testing the Controller class?

Don't test your UIViewControllers. There is a lot of stuff that happens to them that you don't see and/or have no control over. Instead, keep as much logic in other objects such as View Models, and not your UIViewControllers. You can then test your View Models, just like you would test your Models.
Edit:
How you might want to structure your UIViewControllers and test Models & View Models:
The main takeaway from this code is:
Use Dependency Injection
Give real dependencies to classes in Release, and fake dependencies in tests
View Controller
// this class is super simple, so there's hardly any reason to test it now.
class SomeViewController: UIViewController {
#IBOutlet weak var someLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// we give the *real* user accessor in the view controller
let viewModel = SomeViewModel(userAccessor: DBUserAccessor())
someLabel.text = viewModel.welcomeMessage
}
}
User Model
struct User {
var name: String
}
User Accessor
This is some dependency that your View Model needs. Give a real one during Release (in your View Controller). Give a fake one during tests, so that you can control it.
protocol UserAccessor {
var currentUser: User? { get }
}
// since we're using the fake version of this class to test the view model, you might want to test this class on its own
// you would do that using the same principles that I've shown (dependency injection).
class DBUserAccessor: UserAccessor {
var currentUser: User? {
// some real implementation that's used in your app
// get the user from the DB
return User(name: "Samantha") // so not really this line, but something from CoreData, Realm, etc.
}
}
class FakeUserAccessor: UserAccessor {
// some fake implementation that's used in your tests
// set it to whatever you want your tests to "see" as the current User from the "DB"
var currentUser: User?
}
View Model
This is where the actual logic lives that you want to test.
class SomeViewModel {
let userAccessor: UserAccessor
init(userAccessor: UserAccessor) {
self.userAccessor = userAccessor
}
var welcomeMessage: String {
if let username = self.username {
return "Welcome back, \(username)"
} else {
return "Hello there!"
}
}
var username: String? {
return userAccessor.currentUser?.name
}
}
Tests
And finally, how you want to test it.
class SomeViewModelTest: XCTestCase {
func testWelcomeMessageWhenNotLoggedIn() {
let userAccessor = FakeUserAccessor()
let viewModel = SomeViewModel(userAccessor: userAccessor) // we give the *fake* user accessor to the view model in tests
userAccessor.currentUser = nil // set the fake UserAccessor to not have a user "logged in"
// assert that the view model, which uses whatever you gave it, gives the correct message
XCTAssertEqual(viewModel.welcomeMessage, "Hello there!")
}
func testWelcomeMessageWhenLoggedIn() {
let userAccessor = FakeUserAccessor()
let viewModel = SomeViewModel(userAccessor: userAccessor)
userAccessor.currentUser = User(name: "Joe") // this time, the use is "logged in"
XCTAssertEqual(viewModel.welcomeMessage, "Welcome back, Joe") // and we get the correct message
}
}

Related

Protocol Oriented Programming in swift iOS

Here is the Protocols:
protocol WireFrameProtocol{
// router for all normal cases
// like showing login page
}
protocol InteractorProtocol{
var wireFrame: WireFrameProtocol? { get set }
}
protocol HomeWireFrameProtocol: WireFrameProtocol{
// home specific routers
}
protocol HomeInteractorProtocol: InteractorProtocol{
var wireFrame: HomeWireFrameProtocol? { get set }
}
class Test: HomeInteractorProtocol{
var wireFrame: HomeWireFrameProtocol?
}
extension Test: InteractorProtocol{
}
WireFrameProtocol will have all the routing functions. HomeWireFrameProtocol will extend and have some home relating routing only. The test class inherits from HomeInteractorProtocol, which has a var wireFrame: HomeWireFrameProtocol, again HomeWireFrameProtocol is extending WireFrameProtocol.
Is var wireFrame: HomeWireFrameProtocol also represent var wireFrame: WireFrameProtocol?
Ok I realise it now, and fixed my own problem. What I did was
protocol HomeInteractorProtocol: InteractorProtocol{
// do not create another variable to store HomeWireFrame
// var wireFrame: HomeWireFrameProtocol? { get set }
}
The variable wireFrame: WireFrameProtocol can also hold the reference of HomeWireFrameProtocol.
so in Test class I updated:
class Test: HomeInteractorProtocol{
// can use all features from the WireFrameProtocol
var wireFrame: WireFrameProtocol?
// also can use all the feature from HomeWireFrame
// this is kind of what I want to achieve without creating two different variables in the protocols
var homeWireFrame: HomeWireFrameProtocol? {
return wireFrame as? HomeWireFrameProtocol
}
}
extension Test: InteractorProtocol{
}
If I understand your question correctly then you have just encountered a traditional Dimond Problem where it is ambiguous as to which parent class a particular feature is inherited from.
Your view and wireFrame both the variable declared in HomeViewPresenterProtocol and HomeViewInteractorOutputProtocol. So when you confirm these two protocols in HomeViewPresenter the Dimond problem arise. Compiler is confusing to this ambiguous parent.
The easiest solution is to change the variable name as you can't have the same variable or function signature.

Cant typecast variable to specific child class involving Generics

Note: Sorry could not come-up with better title than this, so please
suggest a better one if you come across one after reading the question
I have a BasePresenter class, That should take BaseInteractor and BaseRouter as its init arguments, and each child class of BasePresenter should be able to specify subclass of BaseInteractor and BaseRouter in their implementation
So I have declared my BasePresenter as
open class PRBasePresenter<T: PRBaseInteractor, R: PRBaseRouter> {
var interactor: T!
var router: R!
let disposeBag = DisposeBag()
convenience init(with router : R, interactor : T) {
self.init()
self.router = router
self.interactor = interactor
}
}
So now PRBaseCollectionsPresenter which is a child of PRBasePresenter declares its interactor and router as
class PRBaseCollectionsPresenter: PRBasePresenter<PRBaseCollectionsInteractor, PRBaseCollectionRouter> {
//other code here
}
Obviously PRBaseCollectionsInteractor is a subclass of PRBaseInteractor and PRBaseCollectionRouter is a subclass of PRBaseRouter
Everything works till here fine. Now comes the issue. Every ViewController should have presenter as a property. So I have a protocol which mandates that with
protocol PresenterInjectorProtocol {
var presenter : PRBasePresenter<PRBaseInteractor, PRBaseRouter>! { get set }
}
And my BaseViewController confirms to PresenterInjectorProtocol
public class PRBaseViewController: UIViewController,PresenterInjectorProtocol {
var presenter: PRBasePresenter<PRBaseInteractor, PRBaseRouter>!
//other code...
}
Now lets say I have ChildViewController, it will obviously get presenter because of inheritance, but obviously child would want to have its specific presenter than having a generic one. And obviously in Swift when you override a property you cant change the type of the variable. So the only way is
class PRBaseTableViewController: PRBaseViewController {
var tableSpecificPresenter: PRBaseCollectionsPresenter {
get {
return self.presenter as! PRBaseCollectionsPresenter
}
}
//other code goes here
}
This gives me a warning
Cast from 'PRBasePresenter!' to
unrelated type 'PRBaseCollectionsPresenter' always fails
And trying to ignore it and running will result in crash :(
How can I solve this problem? What am I doing wrong? Or is this approach completely wrong?

Implementing VIPER architecture in iOS

I am implementing my project as per VIPER for the first time and I have some doubts regarding its implementation.This is what I have done so far:
1)Implement Login page
STEPS
i)User taps login button(on view controller).
ii)I have a request model where I store 'username' and 'password'.This is the structure of model:
struct Login{
struct Request{
var txt_email:String!
var txt_password:String!
}
struct Response {
var homeData:Dictionary<String,Any>
}
}
So I pass this Request object to the Interactor.
iii)In Interactor,I have different Workers(Worker class object methods) assigned to perform different tasks such as email validation,empty textFields validation etc.If all is well,the worker api method hits login API and passes the response back to Interactor via delegation.
iv)Update the 'Response' model in the above structure.
v)Now that I have the response in Interactor,I pass this response to the Presenter to do some manipulations according to what controller needs to display to the user.
vi)Pass the data to the Controller and it presents it to user.
Question 1:Am I doing everything right.If no , please show me the right way.If yes , please tell me if there is some room for improvement.
Question 2:I need to implement UITableView and UICollectionView on the home page and I think extensions is the way to go for them.I will follow the same strategy for Home page as well.But suppose , in 'didSelectRowAtIndexPath' , I need to show a pop up to user and I think it will be better for ViewController to ask Presenter directly about the data.But is it the correct way?If not what is the correct way?
Question 3:should I pass data from cellForRowAtIndexPath: to actual cell(MyCell:UITableViewCell) class methods and then assign values to UIElements?Yes or no?
Reference: https://medium.com/#javedmultani16/viper-architecture-viper-64f6cd91e6ec
We developer basically uses the MVC,MVP or MVVM architecture for the development as per the requirement. It is mattering which architecture you are choose to develop the application. Many factors affecting for selection of the software architecture like system designs, requirements, time-lines etc.
In Viper architecture, each block corresponds to an object with specific tasks, inputs and outputs. It is very similar to workers in an assembly line: once the worker completes its job on an object, the object is passed along to the next worker, until the product is finished.
V (View): View is responsible for the UI updates and show whatever the presenter tells it.
I (Interactor) : The Interactor is responsible for fetching data from the model layer, and its implementation is totally independent of the user interface.All the business logic written inside the Interactor. E.g. Get User Data API call written in the Interactor.
P (Presenter): Presenter performing role as intermediator it gets data from interaction and passes to View. (It may be data or any user action)
E (Entity): Basically it is contains the Object Model which is used by Interactor. E.g. Student,Friend,College etc.
R (Router): It contains navigation logic for the application. E.g. Next button action shows second screen.
Morever, I’ve use the PROTOCOL, which contains all the rules and work-flow for the particular module of the application. In iOS all the protocols written in the separate protocol swift file for each module.
Morever, I’ve use the PROTOCOL, which contains all the rules and work-flow for the particular module of the application. In iOS all the protocols written in the separate protocol swift file for each module.
Let’s see the file structure of it:
enter image description here
Benefits:
-All the modules are independent so VIPER is really good for large teams.
-It makes the source code cleaner, more compact and reusable
-It easier to adopt TDD (Test Driven Development)
-You can add easily new features to the existing application without changing other modules possibly.
-It can be applies SOLID principles.
-Reduced number of merge conflicts.
-It Makes it easy to write automated tests since your UI logic is separated from the business logic
struct Person { // Entity (usually more complex e.g. NSManagedObject)
let firstName: String
let lastName: String
}
struct GreetingData { // Transport data structure (not Entity)
let greeting: String
let subject: String
}
protocol GreetingProvider {
func provideGreetingData()
}
protocol GreetingOutput: class {
func receiveGreetingData(greetingData: GreetingData)
}
class GreetingInteractor : GreetingProvider {
weak var output: GreetingOutput!
func provideGreetingData() {
let person = Person(firstName: "David", lastName: "Blaine") // usually comes from data access layer
let subject = person.firstName + " " + person.lastName
let greeting = GreetingData(greeting: "Hello", subject: subject)
self.output.receiveGreetingData(greeting)
}
}
protocol GreetingViewEventHandler {
func didTapShowGreetingButton()
}
protocol GreetingView: class {
func setGreeting(greeting: String)
}
class GreetingPresenter : GreetingOutput, GreetingViewEventHandler {
weak var view: GreetingView!
var greetingProvider: GreetingProvider!
func didTapShowGreetingButton() {
self.greetingProvider.provideGreetingData()
}
func receiveGreetingData(greetingData: GreetingData) {
let greeting = greetingData.greeting + " " + greetingData.subject
self.view.setGreeting(greeting)
}
}
class GreetingViewController : UIViewController, GreetingView {
var eventHandler: GreetingViewEventHandler!
let showGreetingButton = UIButton()
let greetingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.showGreetingButton.addTarget(self, action: "didTapButton:", forControlEvents:.TouchUpInside)
}
func didTapButton(button: UIButton) {
self.eventHandler.didTapShowGreetingButton()
}
func setGreeting(greeting: String) {
self.greetingLabel.text = greeting
}
// layout code goes here
}
// Assembling of VIPER module, without Router
let view = GreetingViewController()
let presenter = GreetingPresenter()
let interactor = GreetingInteractor()
view.eventHandler = presenter
presenter.view = view
presenter.greetingProvider = interactor
interactor.output = presenter
Regarding Question 2 and 3, here's answer with my simple example of Viper app using UITableView: https://stackoverflow.com/a/45709546/3990005.
The ViewController does not speak to the Interactor, but to presenter, so on cellForRowAtIndexPath: you should call the Presenter with information, which IndexPath was selected.
Regarding cell creation, what I like to do is have a setup method in UITableViewCell class, and in ViewController ask Presenter for data and pass it to the cell. Some of it is visible in the above example, and here's cell's setupCell method:
func setupCell(withSong: song) {
titleLabel.text = song.title
descriptionLabel.text = song.description
}

Chain of delegates in Swift

TDLR; I have three classes, when Class A object is updated, it's calling on its delegate (Class B) which is calling on its delegate (Class C) without doing anything else. Class C will use Class B in different ways depending on the values in Class A. Class B need to know of its Class A at touch events. Is this acceptable?
classA { var information: String }
classB { var thing: ClassA thing.delegate = self }
classC { var things: [ClassB] for thing in things { thing.delegate = self } }
My real example
I have three classes: A mapViewController, a mapMarker and a place (model). The map holds multiple mapMarkers, and every mapMarker has a property place, which contains information of what the marker should look like (like place type, "bar", "restaurant" etc). The place might receive new information via a silent push notification, and hence being updated. When the place is updated, I need to notify the mapViewController that the marker needs to be redrawn (I'm using MapBox and their annotations doesn't support redrawing in any way but removing and adding the marker again, since the imageForAnnotation method is a delegate one.)
My first thought was to make two protocols placeDelegate and mapMarkerDelegate.
Place:
protocol PlaceDelegate: class
{
func placeUpdated()
}
class Place {
weak var delegate: PlaceDelegate?
var propertyThatCanBeUpdate: String {
didSet {
//Checking if the newValue == oldValue
delegate.placeUpdated()
}
}
MapMarker
protocol MapMarkerDelegate: class
{
markerShouldReDraw(mapMarker: MapMarker)
}
class MapMarker: PlaceDelegate {
var place: Place!
weak var delegate: MapMarkerDelegate?
init(place: Place) {
self.place = place
place.delegate = place
}
func placeUpdate()
{
delegate.markerShouldReDraw(self)
}
}
MapViewController
class MapViewController {
//I could easily set the marker.delegate = self when adding the markers
func markerShouldReDraw(mapMarker: MapMarker)
functionForRedrawingMarker()
}
This feels a bit ugly, and a bit weird that the MapMarker is just passing the "my place has been updated" information forward. Is this acceptable as far as performance goes? Should I use some kind of NSNotification instead? Should I make the MapViewController the delegate of place and search my array of mapMarker for the one holding the correct place?

Trouble mocking singleton for unit testing in Swift

Hello I am trying to mock one of the singletons I use to test that various view controllers actually call properly it's methods.
I have the singleton declared as such
public class ModelsManager {
static let sharedInstance = ModelsManager()
private init() {}
[...]
}
In the view controllers that use the singleton, it is set to a lazy computed property as such:
class MyViewController: UIViewController {
lazy var Models = {
return ModelsManager.sharedInstance
}()
[...]
}
I am trying to mock the ModelsManager singleton in my XCTestCase as such:
[...]
func testSomething() {
let vc = MyViewController(nibName: "MyView", bundle: nil)
var mockModelsManager = ModelsManagerMock.sharedInstance
vc.Models = mockModelsManager
[... do something that calls a function in ModelsManager...]
expect(mockModelsManager.flag) == true // Using Nimble here
}
class ModelsManagerMock: ModelsManager {
var flag = false
override func test() {
flag = true
}
}
In the expect() assertion I am getting Value of type 'ModelsManager' has no member 'flag'
What am I missing here?
EDIT
It appears that what I was missing was ModelsManagerMock.sharedInstance still returns IRModelsManager() from the superclass. Due to the fact that static can't be overwritten by subclasses, how do I get around this?
The correct solution must involve not subclassing your singleton. Creating a singleton with a private init method prohibits you from subclassing this method.
If the goal is to test the current functionality of the singleton, why do you want to add additional functionality to it? The key point of a singleton is that there should only ever be one. If you want to support more than one, you shouldn't make it a singleton, even if it's just for testing.

Resources