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

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.

Related

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

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.

Why does XCTestCase override the setup method of XCTest?

I'd like to think that I understand the idea of inheritance but apparently I don't because I'm confused as to why there a setup method in XCTestCase if XCTest provides the setup method in its class? XCTestCase is a subclass of XCTest but after reading the Apple docs, it doesn't look any different between the two.
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.
}
}
}
XCTest is a base class, with empty setUp() and tearDown() methods.
XCTestCase inherits from XCTest, so it inherits those same methods. It doesn't have its own implementation of them. They're just do-nothing methods, anyway. They're only defined so that we can override them. This is an example of the Template Method design pattern.
Why define these methods in XCTest, or have XCTest at all? The test runner can work with anything that subclasses from XCTest, and knows nothing about XCTestCase. This potentially allows us to define new ways of defining test suites other than XCTestCase subclasses, while still integrating with the XCTest framework.
For more about xUnit architecture, see JUnit: A Cook's Tour
You can override a method in a subclass to add more functionality to what the superclass has.
You can override the superclass's implementation completely or you can call super.setUp() in the overriding method to execute the superclass's implementation before anything that you add in the override.
In tests, it's common for an XCTestCase subclass to override setUp() to add common setup actions for that class, whereas the superclass's implementation will execute common setup actions for your whole suite. For example, one XCTestCase subclass will have a setUp() method which launches the app, and a subclass of that class will have a setUp() method which calls its superclass setup and then initializes the area under test in that class. In a unit test, this could mean creating an object, or in a UI test, this could mean navigating to a particular page in your app.
You need to override XCTestCase's setUp() when you subclass it if you want something to happen during the setup stage of your tests as it has an empty implementation by default.
The reason that XCTest has a setUp() method defined (even though it does nothing) is to enable other methods on XCTest to call setUp(), for example, invokeTest() on XCTestCase calls setUp(). This enables users of the framework to specify actions to be done at the beginning of each test by overriding the setUp() method, which does not require any test invocation logic, instead of having to override methods with other logic in them, which we wouldn't necessarily know about and may not implement correctly or at all when overriding the method. This model makes setUp() a safe place for you to execute code entirely of your choosing, without needing to worry about whether you have broken the entire test framework.

Is using setUp/tearDown beneficial for unmodified objects in a unit test?

Is using setUp/tearDown beneficial for unmodified objects in a unit test?
I know if the object in question needs to be modified and keep track of its state in any way it would obviously be beneficial to use the setUp/tearDown methods, but what about an object that isn't modified?
I should clarify that myObj in the below example is an object under test.
class MyTests: XCTestCase {
var myObj: MyObj?
override func setUp() {
super.setUp()
myObj = MyObj()
}
override func tearDown() {
myObj = nil
super.tearDown()
}
...
}
vs.
class MyTests: XCTestCase {
let myObj = MyObj()
...
}
EDIT: What sparked this question for me was watching Jon Reid's recent Quality Coding video about TDD: https://youtu.be/4-KrgRH_Yb0?t=7m57s. Notice how he refactors out the sut into setUp and tearDown. I was wondering why he didn't just initialize it when he declared it as an instance variable.
my question is referencing objects that are subject to test. I'm wondering why I should create them in setUp and tearDown as opposed to initializing in declaration
The problem is that in your question the example object referenced is a String. There would be no reason not to initialize a string in a declaration.
However, suppose this is an actual object, such as MyCoolViewController. Now we're talking about something that bears a good deal of state, which may be exactly what we are here to exercise. Clearly we'd like to start with a clean slate each time we run this test (or this suite of tests). Thus it makes a great deal of sense to re-initialize in setUp. And since this thing may be somewhat heavyweight, it makes sense to destroy it tearDown as well.

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