How to unit test that private method is called in Swift - swift

I have a ViewController class that presents a series of two choice popup views. Each two choice popup view is different.
Popup1 - Choice1 -> Choice1Popup
Popup1 - Choice2 -> Choice2Popup
I intend the method to present Popup1 to be public, but I want the other methods that present Choice1Popup and Choice2Popup to be private.
If I decide I need to test Choice1Popup and Choice2Popup then I may have to make them internal instead of private, but they are unlikely to ever be used from any other place.
I want to write a unit test that tests when the button for Choice1 is touched that the method that presents Choice1Popup is called. I've used a protocol with method type variables to allow a Mock to inject the Mock versions of the popup presenters. I'm not feeling 100% comfortable about my approach so I wanted to get input as to whether or not there is a better way.
An aside I'm feeling conflicted about internal versus private. It would be nice to be able to test my private methods but I don't want them to be able to be called from anywhere but a unit test and making them internal exposes them.
Here is the code and a single Unit test is at the bottom:
// protocol to be used by both UserChoices class and UserChoicesMock for method injection
protocol UserChoicesPrivateUnitTesting {
static var choice1Method:(UIViewController) -> Void { get set }
static var choice2Method:(UIViewController) -> Void { get set }
}
// this popup that will be presented with a public method
public class ChoiceViewController:UIViewController {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var subjectLabel: UILabel!
#IBOutlet weak var choice1Button: UIButton!
#IBOutlet weak var choice2Button: UIButton!
var choice1Action:(() -> Void)?
var choice2Action:(() -> Void)?
// ...
}
public class UserChoices: UIViewController, UserChoicesPrivateUnitTesting {
static var choice1Method: (UIViewController) -> Void = choice1
static var choice2Method: (UIViewController) -> Void = choice2
private static func choice1(onTopViewController: UIViewController) {
//present choice1Popup
}
private static func choice2(onTopViewController: UIViewController) {
//present choice2Popup
}
public static func presentChoiceViewController(onTopViewController: UIViewController, ChoiceViewController: ChoiceViewController = ChoiceViewController.instantiateFromAppStoryBoard(appStoryBoard: .MenuStoryboard)) {
let isCustomAnimated = true
// ChoiceViewController.transitioningDelegate = transitionDelegate
ChoiceViewController.choice1Action = { [weak onTopViewController]() in
guard let weakSelf = onTopViewController else {
return
}
weakSelf.dismiss(animated: false, completion: nil)
UserChoices.choice1Method(onTopViewController!)
}
ChoiceViewController.choice2Action = { [weak onTopViewController]() in
guard let weakSelf = onTopViewController else {
return
}
weakSelf.dismiss(animated: false, completion: nil)
UserChoices.choice2Method(onTopViewController!)
}
onTopViewController.present(ChoiceViewController, animated: isCustomAnimated, completion: nil)
}
}
import XCTest
#testable import ChoiceModule
public class UserChoicesMock:UserChoicesPrivateUnitTesting {
static public var choice1Method: (UIViewController) -> Void = choice1
static public var choice2Method: (UIViewController) -> Void = choice2
static var choice1MethodCalled = false
static var choice2MethodCalled = false
static func choice1(onTopViewController: UIViewController) {
choice1MethodCalled = true
}
static func choice2(onTopViewController: UIViewController) {
choice2MethodCalled = true
}
}
class UserChoicesTests: XCTestCase {
func testChoice1CallsPrivateChoice1Method() {
// This is an example of a functional test case.
let vc = UIViewController()
let choiceViewController = ChoiceViewController.instantiateFromAppStoryBoard(appStoryBoard: .MenuStoryboard)
UserChoices.choice1Method = UserChoicesMock.choice1Method
UserChoices.presentChoiceViewController(onTopViewController: vc, ChoiceViewController: choiceViewController)
choiceViewController.choice1Button.sendActions(for: .touchUpInside)
if UserChoicesMock.choice1MethodCalled == false {
XCTFail("choice1Method not called")
}
}
}

Tests can't access anything declared private. They can access anything declared internal as long as the test code does #testable import.
When you get that queasy feeling, "But I shouldn't have to expose this," consider that your class actually has multiple interfaces. There's the "everything it does interface" and there's the "parts needed by production code interface." There are various things to consider about this:
Is there another type that is trying to get out?
Is there another protocol to express a subset of the interface? This could be used by the rest of production code.
Or maybe it's like a home theater amplifier where "controls you don't need that often" are hidden behind a panel. Don't sweat it.

Related

deinit called but the uint testing not finished

I'm trying to test my coordinator flow but the child coordinator deinit called before the unit test case finished
My coordinator class
public final class AppCoordinator: Coordinator {
public var childCoordinators: [Coordinator] = []
public var navigationController: UINavigationController
var window: UIWindow?
public init(window: UIWindow?) {
self.window = window
let secController = SecController()
self.navigationController = UINavigationController(rootViewController: secController)
secController.delegate = self
}
public func start() {
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
}
}
extension AppCoordinator: SecControllerDelegate, SignInControllerDelegate {
public func removeSingIn() {
self.childCoordinators.removeFirst()
}
public func showSignIn() {
let signInCoordinator = SignInCoordinator(navigationController: self.navigationController)
signInCoordinator.delegate = self
self.childCoordinators.append(signInCoordinator)
signInCoordinator.start()
}
}
Unit test class
class AppCoordinatorTests: XCTestCase {
var coordinator: AppCoordinator!
override func setUp() {
super.setUp()
coordinator = AppCoordinator(window: UIWindow())
}
override func tearDown() {
coordinator = nil
super.tearDown()
}
func testStartMethod() {
coordinator.start()
XCTAssertNotNil(coordinator.window?.rootViewController)
}
func testShowSignIn() {
coordinator.showSignIn()
XCTAssertFalse(coordinator.childCoordinators.isEmpty)
XCTAssertTrue(coordinator.navigationController.visibleViewController is SignInController)
}
}
when try to test testShowSignIn always failed because of the deinit call removeSingIn function
public class SignInController: UIViewController {
public weak var delegate: SignInControllerDelegate?
public init() {
super.init(nibName: nil, bundle: nil)
}
deinit {
self.delegate?.removeSingIn()
}
}
Let's review the steps:
testShowSignIn calls coordinator.showSignIn(), where coordinator is an AppCoordinator.
showSignIn() instantiates a SignInCoordinator, and sets its delegate to the AppCoordinator instance.
Now we reach the important part:
We reach the end of showSignIn(). The SignInCoordinator goes out of scope, so Swift destroys it.
Nothing maintains a reference to the SignInCoordinator. But you want to test the interaction between the AppCoordinator and the SignInCoordinator. The code is fighting you, because AppCoordinator decides to create and destroy the SignInCoordinator on its own.
You can test it by changing the design. You have a couple of options.
Option 1: Change AppCoordinator to have a lazy computed property that returns the SignInCoordinator. This can work if you're okay with that design. Then the SignInCoordinator will continue to live, so that the test can query it. This improves the testability of AppCoordinator by exposing the SignInCoordinator.
Option 2: Have the test create a SignInCoordinator and pass it in as an argument to showSignIn(). Then the SignInCoordinator lifecycle will be managed completely outside of AppCoordinator.

VIPER architecture using Swift to store data in presenter

So I'm setting up a simple VIPER architecture in Swift.
The Interactor gets some data from an API, and passes the data to the presenter that then passes formatted data to the view.
The presenter will process the data, and just count the number of objects that are downloaded. To do so I have stored a var in the presenter. The question is should I store data in the presenter?
Interactor:
class Interactor {
weak var presenter: Presenter?
func getData() {
ClosureDataManager.shared.fetchBreaches(withURLString: baseUrl + breachesExtensionURL, completion: { [weak self] result in
guard let self = self else { return }
switch result {
case .failure(let error):
print(error)
case .success(let breaches):
self.presenter?.dataDidFetch(breaches: breaches)
self.presenter?.dataNumberDidFetch(number: breaches.count)
}
})
}
}
Presenter:
class Presenter {
var wireframe: Wireframe?
var view: ViewController?
var interactor: Interactor?
var dataDownloaded = 0
func viewDidLoad() {
print ("presenter vdl")
}
func loadData() {
interactor?.getData()
}
func dataDidFetch(breaches: [BreachModel]) {
view?.dataReady()
}
func showDetail(with text: String, from view: UIViewController) {
wireframe?.pushToDetail(with: text, from: view)
}
func dataNumberDidFetch(number: Int) {
dataDownloaded += number
view?.showData(number: String(dataDownloaded) )
}
}
View (ViewController)
protocol dataViewProtocol {
func showData(number: String)
}
class ViewController: UIViewController, dataViewProtocol {
#IBOutlet weak var showDetailButton: UIButton!
#IBOutlet weak var dataLabel: UILabel!
// weak here means it won't work
var presenter: Presenter?
#IBAction func buttonPressAction(_ sender: UIButton) {
presenter?.loadData()
}
#IBAction func buttonShowDetailAction(_ sender: UIButton) {
presenter?.showDetail(with: "AAA", from: self)
}
func dataReady() {
showDetailButton.isEnabled = true
}
func showData(number: String) {
dataLabel.text = number
}
override func viewDidLoad() {
super.viewDidLoad()
Wireframe.createViewModule(view: self)
presenter?.viewDidLoad()
}
}
Router (Wireframe)
class Wireframe {
static func createViewModule (view: ViewController) {
let presenterInst = Presenter()
view.presenter = presenterInst
view.presenter?.wireframe = Wireframe()
view.presenter?.view = view
view.presenter?.interactor = Interactor()
view.presenter?.interactor?.presenter = presenterInst
}
}
So should the presenter be used to store the number of objects downloaded?
What have you tried I've implemented the var, as shown above. This is a minimum example of the problem.
What resources have you used I've looked on StackOverflow, and Googled the issue. I can't find an answer, but know I could store the data in the view but I think this is incorrect. I could store the number of data in the Interactor, but this also doesn't seem right. It all seems...to violate separation of concerns...
I won't do your homework / use a different architecture / You should use protocols / Why is there a single protocol in your implementation This isn't homework, it is for my own self - study. There may be other architectures that can be used to do this (and coding to protocols is good practice) but this is about storing a variable in the presenter. I want to know if I should store the variable in the presenter, using VIPER and using Swift. Comments about trivia around the question are seldom helpful if they are about variable names, or the like.
What is the question? I want to know if I can store the number of downloaded data items in the presenter.

Am I using the State Pattern correctly?

I'm learning the State Pattern (Finite State Machine)
In the sample project that I built the only way that I figured out to update the UI is to pass a reference of the presenting view to the state machine, and then update the UI from the state I'm working. Am I doing it wrong?😕
Here's my State Machine
class CapturePhotoStateMachine {
var noPictureTakenState: NoPictureTakenState?
var pictureTakenState: PictureTakenState?
var initialState: InitialState?
var vc: SignupAvatarView?
var capturePhotoState: CapturePhotoState?
init(viewController: SignupAvatarView) {
noPictureTakenState = NoPictureTakenState(stateMachine: self)
pictureTakenState = PictureTakenState(stateMachine: self)
initialState = InitialState(stateMachine: self)
vc = viewController
capturePhotoState = initialState
}
func setCapturePhotoState(newState: CapturePhotoState) {
self.capturePhotoState = newState
}
func takePicture() {
self.capturePhotoState?.takePicture()
}
func savePicture(image: UIImage) {
self.capturePhotoState?.savePicture(image: image)
}
func retakePicture() {
self.capturePhotoState?.retakePicture()
}
func setup() {
self.capturePhotoState?.setup()
}
}
Here's my protocol
protocol CapturePhotoState {
func takePicture()
func savePicture(image: UIImage)
func retakePicture()
func setup()
}
Here's a subclass of state
class NoPictureTakenState: CapturePhotoState {
var stateMachine: CapturePhotoStateMachine?
init(stateMachine: CapturePhotoStateMachine) {
self.stateMachine = stateMachine
}
func takePicture() {
stateMachine!.vc?.previewView.isHidden = true
stateMachine!.vc?.capturedImage.isHidden = false
stateMachine!.vc?.saveButton.isHidden = false
stateMachine!.vc?.retakePhoto.isHidden = false
stateMachine?.setCapturePhotoState(newState: (stateMachine?.pictureTakenState)!)
}
func savePicture(image: UIImage) {
}
func retakePicture() {}
func setup() {}
}
The key to your state machine's purpose seems to be that you have interface objects you want to enable or disable depending on the state. That enablement / disablement should be the job of the view controller. The state itself is simply the basis on which questions can be answered such as "What is the current situation" and "What should happen next".
Here's a short simple state machine example that illustrates. It is deliberately trivial. We have just two buttons, and just two states; in each state, exactly one button should be enabled. The states are represented by cases of an enum, and we use a setter observer on that enum to respond whenever the state changes. The enum encapsulates the logic of how many states there are and what the next state is, while the view controller mediates between state change and interface change:
class ViewController: UIViewController {
#IBOutlet weak var takePictureButton: UIButton!
#IBOutlet weak var deletePictureButton: UIButton!
#IBOutlet weak var pictureImageView: UIImageView! // not used in the example
#IBAction func doTakePicture(_ sender: Any) {
// do stuff
doNextState()
}
#IBAction func doDeletePicture(_ sender: Any) {
// do stuff
doNextState()
}
enum State {
case pictureNotTaken
case pictureTaken
var nextState : State {
switch self {
case .pictureNotTaken:
return .pictureTaken
case .pictureTaken:
return .pictureNotTaken
}
}
}
var state : State = .pictureNotTaken {
didSet {
updateInterface()
}
}
func doNextState() {
self.state = self.state.nextState // triggers the setter observer
}
func updateInterface() {
switch state {
case .pictureNotTaken:
takePictureButton.isEnabled = true
deletePictureButton.isEnabled = false
case .pictureTaken:
takePictureButton.isEnabled = false
deletePictureButton.isEnabled = true
}
}
}
Probably what you want is some expansion of that pattern.
the only way that I figured out to update the UI is to pass a reference of the presenting view to the state machine
That is what the above pattern does not do. The setter observer solves that problem for us.
Now, you might object that the switch statement in updateInterface is doing the wrong kind of work. It locates the knowledge of how the interface reflects the state entirely in the view controller. Your impulse is to say that surely that knowledge is part of the state (and that's why you constructed your code the way you did).
My reply would be: well, yes and no. I do sometimes feel that way, and the way I solve the problem is to endow the state machine with properties expressing all the questions the view controller might have about what the current state means as regards the interface. That way, the knowledge is moved to the state, but the interface is still governed, correctly, by the view controller.
So, for example, we might add these two properties to our State enum:
enum State {
// ... everything else is as before ...
var userCanTakePicture : Bool { return self == .pictureNotTaken }
var userCanDeletePicture : Bool { return self == .pictureTaken }
}
So, now, our updateInterface doesn't need any special knowledge about what each state means; it merely asks the state what the interface should be, which is simpler and gives perhaps a more satisfying separation of powers:
func updateInterface() {
self.takePictureButton.isEnabled = state.userCanTakePicture
self.deletePictureButton.isEnabled = state.userCanDeletePicture
}

Keep a class instance allocated until the program ends without using NSObject?

I'm writing a UserNotification library to reuse in many apps. The below .swift file is just dragged into a new project when needed. I'm then adding an NSObject to the View Controller Scene, setting it's custom class to KTUserNotification. I then drag a referencing outlet from this NSObject into the view controller. From there I use this outlet to call Show() to display notifications.
I use the NSObject with a custom class approach because the instance needs to stay allocated until the app ends. I first tried making it just a custom class, using let usernotification = KTUserNotification(). This however deallocated before the call came through to the "shouldPresent notification", causing a EXC_BAD_ACCESS. The #IBInspectables were just a nice addition I tossed in when moving to the NSObject approach.
Is there another way of keeping the instance allocated until the program ends without using an NSObject? Os is the NSObject the best way to go here?
import Cocoa
class KTUserNotification: NSObject, NSUserNotificationCenterDelegate {
#IBInspectable var showEvenIfAppIsFocus: Bool = true
#IBInspectable var title: String = ""
#IBInspectable var subTitle: String = ""
#IBInspectable var informativeText: String = ""
#IBAction func showNotification(_ sender: Any) {
Show()
}
override func awakeFromNib() {
NSUserNotificationCenter.default.delegate = self
}
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool {
return showEvenIfAppIsFocus
}
public func Show(title : String, subTitle: String, informativeText : String) {
let notification = NSUserNotification()
let randIdentifier = Int(arc4random_uniform(999999999) + 1)
notification.identifier = "KTUserNotification\(randIdentifier)"
notification.title = title
notification.subtitle = subTitle
notification.informativeText = informativeText
notification.soundName = NSUserNotificationDefaultSoundName
let notificationCenter = NSUserNotificationCenter.default
notificationCenter.deliver(notification)
}
public func Show() {
Show(title: title, subTitle: subTitle, informativeText: informativeText)
}
}

How to pass data from one model to another in Swift?

I am working on a project containing a "Create New Account" view controller with its accompanying Swift class called "CreateNewAccount." The user can place 4 input values into this view controller, a first name, last name, user name, and password. Upon clicking the "Create Account" button in this VC, the 4 input values are passed on to a Swift class (within the model layer of MVC, I believe) called UserInfoRetrieveModel where they are supposedly stored.
I would then like to pass these values to another Swift class (that is a model as well) called UserInfoModel, which will then delegate out the first name value to the text value of label located in a VC called "ThanksForJoining" (and its accompanying class).
I have figured out how to pass values from VC to model (CreateNewAccount to UserInfoRetrieveModel) and from model to VC (UserInfoModel to ThanksForJoining), but somewhere in my transference from model to model (UserInfoRetrieveModel to UserInfoModel) the values initially inputted into "CreateNewAccount," which I would like to pass over to the second model class UserInfoModel become nil.
Below is the code for CreateNewAccount, UserInfoRetrieve, UserInfo, and ThanksForJoining:
CreateNewAccount ->
import UIKit
class CreateNewAccount: UIViewController{
#IBOutlet weak var FNInput: UITextField!
#IBOutlet weak var LNInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
var uInfoRetrieve = UInfoRetrieveModel()
#IBAction func thanksForJoining(_ sender: Any) {
uInfoRetrieve.firstName = FNInput.text!
uInfoRetrieve.lastName = LNInput.text!
uInfoRetrieve.userName = usernameInput.text!
uInfoRetrieve.password = passwordInput.text!
uInfoRetrieve.delegate = self
uInfoRetrieve.retrieving()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension CreateNewAccount: UInfoRetrieveModelDelegate{
func credentialTransfer(data: String) {
print(data)
}
}
UserInfoRetrieve ->
import Foundation
protocol UInfoRetrieveModelDelegate: class {
func credentialTransfer(data:String)
}
class UInfoRetrieveModel: NSObject {
weak var delegate: UInfoRetrieveModelDelegate?
var firstName: String = ""
var lastName: String = ""
var userName: String = ""
var password: String = ""
func retrieving(){
delegate?.credentialTransfer(data: firstName)
delegate?.credentialTransfer(data: lastName)
delegate?.credentialTransfer(data: userName)
delegate?.credentialTransfer(data: password)
}
}
UserInfo ->
import Foundation
protocol UserInfoModelDelegate: class {
func didReceiveDataUpdate(data: String)
}
class UserInfoModel {
weak var delegate: UserInfoModelDelegate?
let uInfoRetrieve = UInfoRetrieveModel()
func requestData() -> Array<String> {
let firstName = uInfoRetrieve.firstName
let lastName = uInfoRetrieve.lastName
let userName = uInfoRetrieve.userName
let password = uInfoRetrieve.password
delegate?.didReceiveDataUpdate(data: firstName)
delegate?.didReceiveDataUpdate(data: lastName)
delegate?.didReceiveDataUpdate(data: userName)
delegate?.didReceiveDataUpdate(data: password)
let credentials = [firstName, lastName, userName, password] as [Any]
return credentials as! Array<String>
}
}
ThanksForJoining ->
import UIKit
class ThanksForJoining: UIViewController {
var userInfo = UserInfoModel()
#IBOutlet weak var firstName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userInfo.delegate = self
firstName.text = userInfo.requestData()[0]
print("yo")
print(userInfo.requestData()[0])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ThanksForJoining: UserInfoModelDelegate {
func didReceiveDataUpdate(data: String) {
print(data)
}
}
UserInfoModel and CreateNewAccount do both create a new instance of UInfoRetrieveModel. You have to connect them properly for them to pass on information.
Connecting properly means (in the simplest form) one constructs the other and sets itself as the delegate of the other, so UInfoRetrieveModel can pass on data. The constructing of a child model is usually done via a computed property.
Example
struct Account {
let firstName: String, lastName: String
let userName: String, password: String
}
extension UInfoRetrieveModelDelegate: class {
createAccount(_ account: Account): Bool
}
extension UserInfoModel: UInfoRetrieveModelDelegate{
func createAccount(_ account: Account) -> Bool {
// Handling creation of account.
return success == true
}
var newUInfoRetrieveModel: UInfoRetrieveModel {
let helperModel = UInfoRetrieveModel(parent: self)
helperModel.delegate = self
return helperModel
}
}
Explanation
Yes. You usually have a Model, your data, then have something that controls access to it to make changes on your model, manages how the model is stored, maybe syncing with a cloud-service, thats the ModelController which you pass around between ViewControllers, more/other controllers you usually use incase that makes things simpler. In your case you would probably pass createAccount(the call) on to a controller/viewController which is in charge of telling the modelController to create the account and then telling one of its views/viewControllers to display the modal/whatever.
The usual way to pass data to a higher level is to have for the viewController/controller a delegate it uses to communicate with higher up, the one “responsible for actions the ViewController/controller cannot do by itself”, eg pushing data up(creation calls, modification calls, deletion calls) if it makes no sense to give it a modelController since its not control of that part of the application, etc. In your case you can of course pass a modelController to each little viewController/view, but its usually more practical/simpler to only give it to the one controlling the part and let others communicate with that currently-that-part-controlling controller/viewController.
More partical here means that you may not want CreateAccountViewController to display the success dialog, but rather another, which CreateAccountViewController can then do not by itself since it’s not on the stack anymore.