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

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.

Related

How to test an instance method that calls an async function defined outside my project?

I need to test a method of a custom view controller. The method looks something like this:
func generateToken() {
// Some validations
let delegate = TokenizationDelegate() // This class is defined in an external dependency
delegate.tokenizeCard(model: cardModel) { response in
// Business logic
} failBlock: { error in
// Show error view
}
}
What would be the best way to test this? I was trying to use a XCTestExpectation, but the generateToken() method doesn't take any parameters so I can pass a closure where I can fulfill the expectation...
I'm trying to achieve a 100% coverage on this method, and right now I see in the coverage report that the closures aren't covered.
Preferably I would not want to modify the signature of the method generateToken(), but if there's no other way to test it, I'm open to hear suggestions!

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)
}
}

Unit testing methods which update UI elements in 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.)

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 run one-time setup code before executing any XCTest

I have the following problem. I want to execute a piece of code before all test classes are executed. For instance: I don't want my game to use the SoundEngine singleton during executing, but the SilentSoundEngine. I would like to activate the SilentSoundEngine one time not in all tests. All my tests look like this:
class TestBasketExcercise : XCTestCase {
override func setUp() {
SilentSoundEngine.activate () // SoundEngine is a singleton
}
// The tests
}
-Edit-
Most of the answers are directed at providing custom superclass for the TestCase. I am looking for a more general and cleaner way to provide the environment that all tests need to execute. Isn't there a "main" function/ Appdelegate like feature somewhere for tests?
TL;DR:
As stated here, you should declare an NSPrincipalClass in your test-targets Info.plist. Execute all the one-time-setup code inside the init of this class, since "XCTest automatically creates a single instance of that class when the test bundle is loaded", thus all your one-time-setup code will be executed once when loading the test-bundle.
A bit more verbose:
To answer the idea in your edit first:
Afaik, there is no main() for the test bundle, since the tests are injected into your running main target, therefore you would have to add the one-time-setup code into the main() of your main target with a compile-time (or at least a runtime) check if the target is used to run tests. Without this check, you'd risk activating the SilentSoundEngine when running the target normally, which I guess is undesirable, since the class name implies that this sound-engine will produce no sound and honestly, who wants that? :)
There is however an AppDelegate-like feature, I will come to that at the end of my answer (if you're impatient, it's under the header "Another (more XCTest-specific) approach").
Now, let's divide this question into two core problems:
How can you ensure that the code you want to execute exactly one time when running the tests is actually being executed exactly one time when running the tests
Where should you execute that code, so it doesn't feel like an ugly hack and so it just works without you having to think of it and remember necessary steps each time you write a new test suite
Regarding point 1:
As #Martin R mentioned correctly in his comments to this answer to your question, overriding +load is not possible anymore as of Swift 1.2 (which is ancient history by now :D), and dispatch_once() isn't available anymore in Swift 3.
One approach
When you try to use dispatch_once anyway, Xcode (>=8) is as always very smart and suggests that you should use lazily initialized globals instead.
Of course, the term global tends to have everyone indulge in fear and panic, but you can of course limit their scope by making them private/fileprivate (which does the same for file-level declarations), so you don't pollute your namespace.
Imho, they are actually a pretty nice pattern (still, the dose makes the poison...) that can look like this, for example:
private let _doSomethingOneTimeThatDoesNotReturnAResult: Void = {
print("This will be done one time. It doesn't return a result.")
}()
private let _doSomethingOneTimeThatDoesReturnAResult: String = {
print("This will be done one time. It returns a result.")
return "result"
}()
for i in 0...5 {
print(i)
_doSomethingOneTimeThatDoesNotReturnAResult
print(_doSomethingOneTimeThatDoesReturnAResult)
}
This prints:
This will be done one time. It doesn't return a result.
This will be done one time. It returns a result.
0
result
1
result
2
result
3
result
4
result
5
result
Side note:
Interestingly enough, the private lets are evaluated before the loop even starts, which you can see because if it were not the case, the 0 would have been the very first print. When you comment the loop out, it will still print the first two lines (i.e. evaluate the lets).
However, I guess that this is playground specific behaviour because as stated here and here, globals are normally initialized the first time they are referenced somewhere, thus they shouldn't be evaluated when you comment out the loop.
Another (more XCTest-specific) approach
(This actually solves both point 1 and 2...)
As the company from Cupertino states here, there is a way to run one-time-pre-testing setup code.
To achieve this, you create a dummy setup-class (maybe call it TestSetup?) and put all the one time setup code into its init:
class TestSetup: NSObject {
override init() {
SilentSoundEngine.activate()
}
}
Note that the class has to inherit from NSObject, since Xcode tries to instantiate the "single instance of that class" by using +new, so if the class is a pure Swift class, this will happen:
*** NSForwarding: warning: object 0x11c2d01e0 of class 'YourTestTargetsName.TestSetup' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector +[YourTestTargetsName.TestSetup new]
Then, you declare this class as the PrincipalClass in your test-bundles Info.plist file:
Note that you have to use the fully qualified class-name (i.e. YourTestTargetsName.TestSetup as compared to just TestSetup), so the class is found by Xcode (Thanks, zneak...).
As stated in the documentation of XCTestObservationCenter, "XCTest automatically creates a single instance of that class when the test bundle is loaded", so all your one-time-setup code will be executed in the init of TestSetup when loading the test-bundle.
From Writing Test Classes and Methods:
You can optionally add customized methods for class setup
(+ (void)setUp) and teardown (+ (void)tearDown) as well, which run before
and after all of the test methods in the class.
In Swift that would be class methods:
override class func setUp() {
super.setUp()
// Called once before all tests are run
}
override class func tearDown() {
// Called once after all tests are run
super.tearDown()
}
If you want to call the setUp method only once for all UI Tests in the class, in Xcode 11 you can call override class func setUp()
This approach is part of Apple documentation for UI testing:
https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods
If you build a superclass for your test case to be based on, then you can run a universal setup in the superclass and do whatever specific setup you might need to in the subclasses. I'm more familiar with Obj-C than Swift and haven't had a chance to test this yet, but this should be close.
// superclass
class SuperClass : XCTestCase {
override func setUp() {
SilentSoundEngine.activate () // SoundEngine is a singleton
}
}
// subclass
class Subclass : Superclass {
override func setUp() {
super.setup()
}
}

Resources