Unit testing methods which update UI elements in iOS - ios

I am trying to write unit tests for my existing code.
I have 3 methods.
func methodOne() {
// code
methodTwo()
}
func methodTwo() {
// code
methodThree()
}
func methodThree() {
// code
// update UI element
}
What's the ideal way to unit test such methods. While unit testing methodTwo() it calls methodThree() as the UI elements are not loaded they have nil value when methodThree() is called. How to unit test methods which involve UI elements. I don't want to test if the UI element is loaded properly, I just want to test the code in methodTwo() and methodThree(). Is there a way to bypass UI elements related code. Any help is appreciated. Thank you.

The best way to do this would be to separate the code you want to test from your UI as much as you can. This could be done by putting the //code part of methodTwo into its own function (ie methodFour) and calling it from methodTwo. Then your code would look like this:
func methodTwo() {
methodFour()
methodThree()
}
func testMethodFour() {
methodFour()
// check assumptions
}
func methodFour() {
// code that you originally had in methodTwo before methodThree
}

Hi #Pradeep 👋, welcome to StackOverflow.
From the way you describe the failures you are experiencing I'm guessing your methods are #IBOutlets in a UIViewController, it that correct?
If that's the case, you can make the tests initialize the UIViewController view by doing something like this before calling the methods that depend on the view being available:
_ = viewControllerUnderTest.view
or
viewControllerUnderTest.beginAppearanceTransition(true, animated: false)
This is a decent post to learn more about the gotchas of testing UIViewControllers, this has a more advanced approach that could help you if you end up writing a lot of tests with dependencies on the view like you described.
I would encourage you, though, to keep as much business logic code as you can aways from UIViewControllers and UIViews. The lifecycle of these classes makes them a bit trickier to test, and the simplest you can make something to test the better.
Write all your business logic in dedicated classes and structs, they will be simpler to test if they don't subclass other types. Use UIViewControllers only as glue code to show data to the user and forward their inputs to your business logic, so that you'll have to write only a few straightforward tests. Keep the UIViews humble, with only logic to configure them.
(If my UIViewController guess was wrong, please add more details and we'll figure out a way to get your tests to not crash.)

Related

How can I utilize dependency injection in Swift with minimal reliance on external libraries?

I am trying to understand how dependency injection works in Swift so I can inject into my ViewController. I have some experience with it in Kotlin but am unsure of how to approach it in Swift. I would prefer to have minimal reliance on external libraries but am not opposed to using one or two if they are reliable and well-maintained. Here is some code I found on a blog post about DI in Swift.
import UIKit
class ViewController: UIViewController {
lazy var requestManager: RequestManager? = RequestManager()
}
// Initialize View Controller
let viewController = ViewController()
// Configure View Controller
viewController.requestManager = RequestManager()
I understand that instead of instantiating RequestManager in the ViewController, I can create an instance of the ViewController itself and instantiate it using property access but I don't understand where I should declare the instance of the ViewController. I feel like I am missing some code or some context. Can someone help me to understand this?
To quickly answer the exact question asked, of how to pass in variables to VC's inside a storyboard. You can simply do something like this in the previous viewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let vc = segue.destination as? SomeViewController else {
return
}
vc.requestManger = SomeRequestManger()
}
To give a longer, more opinionated answer:
I personally only use DI in very small and very limited ways. There are a few exceptions where more advanced use cases crop up. But in general I feel like a lot of people way over think this and just do it because they "should" as oppose to actually needing too. A lot of cases I see online of creating mock instances of every service class, and passing them around where needed, is simply a bad idea as you wind up testing very little of your actual code, and create a tonne of extra effort for yourself for no reason.
For an app i'm working on, all I did was create a mock instance of URLProtocol and hold onto a reference of a separate URLSession instance in a class in my test bundle, something like this:
public let mockURLSession: URLSession
private init() {
let sessionConfig = URLSessionConfiguration.ephemeral // Uses no caching / storage
sessionConfig.protocolClasses = [MockURLProtocol.self]
mockURLSession = URLSession(configuration: sessionConfig)
...
}
and then I had a DependencyManger.swift that was a singleton. Any Service class that needed a URLSession, was created and held inside that class, and I had a function to pass the mock session or the real session into each of those classes, from the manager.
From then on I didn't have to worry about adding 20K lines of code of third party libraries, just so I could have fancy Init's on my VC's. Or worry about writing hundreds of lines of code passing references between one viewController to another. Inside each VC I would just do:
DependencyManger.shared.networkService.get(...) {
...
}
All I need to do then is flip the toggle while running my tests, and all my networking code will use my MockURLProtocol, where I can override the methods and have it return stubbed data per URL. In this situation I only needed to mock my networking data, if you need to also mock push notifications for example, you'd need to go one step further.
Incredibly simple, works with interface builder or UI written in code, extremely flexible, a tiny amount of code, no third party dependencies to maintain

How to replicate an abstract Fragment in Kotlin to View Controllers in Swift

I am working on implementing an iOS version of an existing Kotlin Android app. The specific feature that I am reproducing is a stepped wizard workflow.
How I Did it in Android
In Android, the library that I used had the following process:
Add a "stepper" view to a root activity, and provide a StepAdapter to this view
The StepAdapter controls the number of steps, and returns each step on request by index.
Each step is a Fragment implementing a Step interface that provides hooks for validation and transitioning between steps.
I ended up creating an abstract Fragment type that provides default implementations for the Step methods:
abstract class StepFragment(private val index: Int) : Fragment(), Step {
...overriding some methods from Fragment
...implementing methods from Step
}
Each step's Fragment inherits this class, passing a hard coded index into StepFragment's constructor. The steps may override some of the methods from StepFragment, and some of them call the superclass's implementation as well (I use the #CallSuper annotation to facilitate this). The StepAdapter instantiates and returns each one of these fragments.
How I'm Trying to Do it in iOS
Back in the iOS/Swift world, I found a similar library, but all it provides is the view for the progress bar. It does not handle instantiating and displaying the content of each step like the Android one does. I need to handle that myself.
You can, however, provide a delegate to the stepper that allows you to hook into the transitions (willSelectIndex, didSelectIndex, canSelectIndex, etc.). I made the root ViewController the delegate for the stepper. This ViewController has the progress bar view at the top, and a container for each step beneath the progress bar. Each step's ViewController is embedded into a corresponding container. To control transitions between steps, I simply show and hide these container views. I have that part figured out.
What I do not have figured out is how to replicate the StepFragment class from Android Fragments into Swift ViewControllers. I have the following problems:
Swift does not have abstract classes (for some reason).
I do not instantiate the step ViewControllers; that is handled internally by the storyboard. This means that I can't provide the step index in the constructor like I can in Android. An abstract property would be the typical way to get around this, but (see above) Swift doesn't have abstract classes.
I could get around these problems in a few ways:
Instead of an abstract class, I can use a protocol. I can specify the requirement that all classes that conform to the protocol must be ViewControllers, but I cannot override methods from ViewController in the protocol (as far as I've been able to see). I can add an extension to the protocol to provide functions with default implementations, which implementors can then override, and they can even call into the original functions (using (self as Protocol).func()). However, this feels very clunky.
Instead of an abstract class, I can use a regular class. I can achieve everything that I want to here, except that I can't enforce that the subclass implements abstract members at compile time. I would need to use something like fatalError() in the base class, which only throws at runtime. This also feels clunky.
I keep hearing about the "delegate" pattern, and I think it's a nice elegant way to solve a lot of problems in a "Swifty" way, but I have no idea how I would use that here. I think I would prefer being able to do it this way over the above two.
TL;DR
To sum up my problem, I am looking for a way for multiple "step" child ViewControllers to hook into a wizard workflow controlled by a parent ViewController. They should be able to perform some (potentially async) logic on step selection, before transitions, and even block transitions. I must be able to optionally ignore some of these hooks, which should then use a default implementation.
I discovered that you actually can provide default implementations for protocol functions, so this is the solution I came to:
protocol StepViewController: UIViewController {
var stepIndex: Int { get }
func onBackClicked(_ goToPreviousStep: () -> Void)
...
}
extension StepViewController {
// this is a default implementation, so implementors don't have to provide it
func onBackClicked(_ goToPreviousStep: () -> Void) {
goToPreviousStep()
}
...
}
class StepOneViewController: UIViewController, StepViewController {
let stepIndex: Int = 0
func onBackClicked(_ goToPreviousStep: () -> Void) {
// if I want to call the default implementation I can do this:
(self as StepViewController).onBackClicked(goToPreviousStep)
}
}

In Unit Tests how can I programmatically dismiss system permission dialog?

I have an unit test which calls methods on CNContactStore() e.g. CNContactStore().execute(saveRequest). So the permission dialog for contacts pops up, like the Push notifications alert but the contacts permission dialog doesn't get dismissed automatically. I know how to do this in UI tests with addUIInterruptionMonitor() but have no idea how to do this in unit test.
I would create a wrapper around CNContactStore and then use a mock when testing.
You're not really interested in testing CNContactStore, you are interested in testing that your code interacts with CNContactStore properly right?
Setup
I would start out creating protocols and classes to extract the contact stuff out of your "normal" code base.
First a Contact struct to hold the properties you need later to create an actual CNContact
struct Contact {
//holds whichever properties you need to create a CNContact
}
Then a protocol to hold the methods you would like to execute. This could be done with a protocol with a lot of methods like so
protocol ContactsHolder {
func save(contact: Contact)
func add(contact: Contact)
func delete(contact: Contact)
func update(contact: Contact)
//Maybe more methods, the important thing is that you abstract yourself away from CNContactStore and other Contact kit classes
}
Or you could create an enum holding the possible options like so
enum ContactsUpdateMethod {
case save(Contact)
case add(Contact)
case delete(Contact)
case update(Contact)
}
protocol ContactsHolder {
func execute(_ method: ContactsUpdateMethod)
}
In Your "Real" Code
With that in place, you are ready to create your actual ContactsHolder, which then internally uses CNContactStore and everything related to that framework.
So for instance (if you chose the version with a "pure" save function)
class CNContactsHolder: ContactsHolder {
func save(contact: Contact) {
//1. create a `CNContact` from your `Contact`
//2. create a saveRequest
//3. execute: CNContactStore().execute(saveRequest)
}
....
}
And then you give the class(es) who needs to work with CNContactStore a reference to your new ContactsHolder protocol
So in your class you have
let contactsHolder: ContactsHolder
And then you can either pass it in, in your init method
init(contactsHolder: ContactsHolder = CNContactsHolder()) {
self.contactsHolder = contactsHolder
}
Or you can declare it as a var and then give it a default value
So instead of:
let contactsHolder: ContactsHolder
You say:
var contactsHolder: ContactsHolder = CNContactsHolder()
The important thing is that you can change the ContactsHolder from being a "real" CNContactsHolder into a mock when you need to test
In Your Test Code
To test this, you create a mock:
struct MockContactsHolder: ContactsHolder {
var saveWasCalled = false
func save(contact: Contact) {
saveWasCalled = true
}
}
And then you use that in your class instead of the CNContactsHolder
Now you should be able to test your own code, without getting interrupted with permissions and stuff that is not relevant to your code, but is a consequence of using CNContactStore.
Disclaimer :)
I haven't run the above by a compiler, so there may be typos.
Also, there might be bits and pieces missing to make it fit to CNContact (callbacks and so on), but I hope you get the idea about how to split things apart.
And finally...it may seem like a lot of work, but I think it makes sense to get the "framework specific" code out into a separate helper class, hid behind a protocol, so that you can swap it out whenever you need to do testing for instance, or...if you decide to get rid of CNContact at a later point and use a different frameworks.
Hope it helps.
I think you're confusing Unit Testing with UI Testing. In Unit Testing, you just want to test, your codes (e.g. functions and properties) and with that, you'll most probably need to have "mock-up".
For instance, you want to test your login button selector that has a network calls after validation of the input fields.
The following should be the steps:
Test your validation logic. Both failing and succeeding cases.
Test the code inside the completion block of your API call, BUT not using the REAL API data. Instead, use your mocked API here.
and so on...
Now, back to your question, you don't need to handle that uncontrollable and "un-dismissable" alert controller generated by the system. Instead, what you wanna do is to "mock" (ughh not again) that pop-up event by hitting the delegate function for that access-contacts alert by the system, "mock" a response namely "Don't Allow" and "OK". What do you expect to happen when user taps on the first button? The second button? Set expectations/assert.
That's it. Hit every function you need to hit to increase the coverage of your code. Let me know if this helps.

How to create a test that passes when a function is triggered?

I am trying to unit test a two functions in a view controller class. The two functions will create a user and sign in a user respectively. My tests are not UI related.
As of now, I simply need to create a test that passes when one of the functions is called. The goal is to do this without changing the current view controller implementation if possible and just keep it all in the testing class/function.
How do I go about this?
Thanks
I'm assuming a view controller with a method that invokes either "create user" or "sign in user". (You say it's not UI related, but we can easily test button taps if that's the trigger.)
class MyViewController: UIViewController {
func triggeringMethod() {
// Calls one of the two methods below
}
func createUser() {
// Does stuff
}
func signInUser() {
// Does stuff
}
}
And it sounds like you want to test the flow, but not the effect. That is, you want to test "Was createUser() called?" There are several ways to get what you want, but the better ways will require you to change your view controller implementation.
Making a Partial Mock
A standard trick from Working Effectively With Legacy Code by Michael Feathers is "Subclass and Override Method". Let's start there. In test code, we can make
class TestableMyViewController: MyViewController {
override func createUser() {
}
override func signInUser() {
}
}
So far, this is a way of stubbing out the effects of these methods. But we can now add the mocking techniques from my try! Swift Tokyo talk.
class TestableMyViewController: MyViewController {
var createUserCallCount = 0
var signInUserCallCount = 0
override func createUser() {
createUserCallCount += 1
}
override func signInUser() {
signInUserCallCount += 1
}
}
Now you can call the triggering method, and check the call counts.
(Changes you may have to make: The class can't be final. The methods can't be private.)
Moving the Workers
While this is a fine place to start, don't stop there. What we've created is a "partial mock". That's where we've kept most of the functionality, but mocked out a couple of the methods. This is something to avoid. The reason is that we end up with class that mixes production code and test code. It would be far too easy to end up in a situation where you're inadvertently testing test code instead of testing production code.
What the partial mock makes clear is that we're missing a boundary. The view controller is doing too much. The actual work of "create user" and "sign in user" should be performed by another type (possibly even 2 types). In Swift, we can define this boundary with a protocol. That way production code can use the real functionality, while for test code we can inject mocks.
This means the production code should avoid deciding for itself who does the actual work. Instead, we should tell it who does the work. That way, tests can provide alternative workers. Specifying these dependencies from the outside is called "Dependency Injection".
Passing Back Effects
Another option lets us avoid mocks altogether. Instead of testing whether something was called, we can describe the desired effect in an enumeration. Then we can define effects like
enum Effect {
case createUser(CreateUserRequestModel)
case signInUser(SignInUserRequestModel)
}
Instead of the triggering method calling createUser() or signInUser(), it would call a delegate. (Another option is to pass in closures instead of specifying delegates.)
protocol Delegate {
perform(_ effect: Effect)
}
Then in the triggering method,
delegate?.perform(.createUser(parameters))
This means it's up to the actual delegate to transform these enumeration values into actual work. But it makes the tests easy to write. All we need is to provide a testing implementation that captures the Effect value.

How is the setUp() method called before the invocation of each test method?

I'm going through a great book learning about test-driven development in Swift. My ultimate goal is to get a better understanding of OOP architecture. As I'm reading the book, a section early on states that the setUp() method is fired before each test method which I understand does the setup of objects to run the test for a pass or fail result. What I'm unsure of is how is this even possible I'm guessing from an architecture stand-point? How was Apple able to make a class that has a method which is fired before every other method in the class?
Here is some sample code:
import XCTest
#testable import FirstDemo
class FirstDemoTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
I think XCTest class has a lifecycle just like UIViewController for example.
Same as viewDidLoad() called after init(coder:) when the view is loaded into memory, setUp() method is also called after the test class is loaded (once during the life of the object).
You can also investigate native and third party test frameworks source code on github:
https://github.com/apple/swift-corelibs-xctest
https://github.com/google/googletest
You are Subclassing a Class called XCTestCase. Usally Testframework introspects Classes in which Test-Methods are defined and run them in a particular order.
Anyway, im not 100% sure if the XCTest is working this way, but you could try to have a look at the source code and try to dig deeper there:
https://github.com/apple/swift-corelibs-xctest
For each test to be tested: setup() is called, then the actual test, then teardown().
Then again setup another test from that class and tearDown again...until all** tests of your test class get ran.
The sequence of the lines won't affect which test gets called first.
The reason that it's made this way is because 2 tests should be 100% independent. You don't want testExample do something to the object/sut/system_under_test (mutate its state) you're performing your tests on but, not undo what it's done. Otherwise your testPerformanceExample would be loaded from a state you weren't
expecting.

Resources