How to stub Swift "Trait/Mixin" method in struct/class for testing - swift

I have recently read about how to add "Traits/Mixins" to a struct/class in Swift by creating a protocol and extending that protocol with a default implementation. This is great as it allows me to add functionality to view the controller without having to add a bunch of helper objects to said view controller. My question is, how do I stub calls that are provided by these default implementations?
Here is a simple example:
protocol CodeCop {
func shouldAllowExecution() -> Bool
}
extension CodeCop {
func shouldAllowExecution() -> Bool {
return arc4random_uniform(2) == 0
}
}
struct Worker : CodeCop {
func doSomeStuff() -> String {
if shouldAllowExecution() {
return "Cop allowed it"
} else {
return "Cop said no"
}
}
}
If I wanted to write two tests, one that verifies that the String "Cop allowed it" is returned by doStuff() when CodeCop does not allow execution, and another test that verifies that the String "Cop said no" is returned by doStuff() when CodeCop does not allow execution.

This is simple enough to do by writing an additional protocol in your test target, called CodeCopStub, that inherits from CodeCop:
protocol CodeCopStub: CodeCop {
// CodeCopStub declares a static value on the implementing type
// that you can use to control what is returned by
// `shouldAllowExecution()`.
//
// Note that this has to be static, because you can't add stored instance
// variables in extensions.
static var allowed: Bool { get }
}
Then extend CodeCopStub's shouldAllowExecution() method, inherited from CodeCop, to return a value depending on that new static variable allowed. This overrides the original CodeCop implementation for any type that implements CodeCopStub.
extension CodeCopStub {
func shouldAllowExecution() -> Bool {
// We use `Self` here to refer to the implementing type (`Worker` in
// this case).
return Self.allowed
}
}
All you have left to do at this point is to make Worker conform to CodeCopStub:
extension Worker: CodeCopStub {
// It doesn't matter what the initial value of this variable is, because
// you're going to set it in every test, but it has to have one because
// it's static.
static var allowed: Bool = false
}
Your tests will then look something like this:
func testAllowed() {
// Create the worker.
let worker = Worker()
// Because `Worker` has been extended to conform to `CodeCopStub`, it will
// have this static property. Set it to true to cause
// `shouldAllowExecution()` to return `true`.
Worker.allowed = true
// Call the method and get the result.
let actualResult = worker.doSomeStuff()
// Make sure the result was correct.
let expectedResult = "Cop allowed it"
XCTAssertEqual(expectedResult, actualResult)
}
func testNotAllowed() {
// Same stuff as last time...
let worker = Worker()
// ...but you tell it not to allow it.
Worker.allowed = false
let actualResult = worker.doSomeStuff()
// This time, the expected result is different.
let expectedResult = "Cop said no"
XCTAssertEqual(expectedResult, actualResult)
}
Remember that all of this code should go in your test target, not your main target. By putting it in your test target, none of it will affect your original code, and no modification to the original is required.

I am not sure if that is what you are looking for, but one way you can test this behavior without updating your code is by updating your project structure the following way:
Keep the CodeCop protocol in one file (let's say CodeCop.swift) and add the extension code into another one (CodeCop+shouldAllowExecution.swift)
While CodeCop.swift is linked to both your main target and your test target, CodeCop+shouldAllowExecution.swift is only in the main target.
Create a test file CodeCopTest.swift, only available in the test target, who contains another default implementation of shouldAllowExecution that will help you run your test.
Here's a potential CodeCopTest.swift file
import XCTest
fileprivate var shouldCopAllowExecution: Bool = false
fileprivate extension CodeCop {
func shouldAllowExecution() -> Bool {
return shouldCopAllowExecution
}
}
class PeopleListDataProviderTests: XCTestCase {
var codeCop: CodeCop!
override func setUp() {
super.setUp()
codeCop = CodeCop()
}
override func tearDown() {
codeCop = nil
super.tearDown()
}
func testWhenCopAllows() {
shouldCopAllowExecution = true
XCTAssertEqual(codeCop.doSomeStuff(), "Cop allowed it", "Cop should say 'Cop allowed it' when he allows execution")
}
func testWhenCopDenies() {
shouldCopAllowExecution = false
XCTAssertEqual(codeCop.doSomeStuff(), "Cop said no", "Cop should say 'Cop said no' when he does not allow execution")
}
}

Related

Swift Unit Test function that async dispatches a block that sets a variable I want to test

I have some code that is like the following
class Vibration: NSObject {
var status: VibrationStatus // an enum
}
and a function on another class (of type NSObject) like the following, that is part of an object that has a property vibration of type Vibration
func vibrate() {
DispatchQueue.main.async { [weak self] in
vibration.status = .vibrating
// do some real HW vibrate stuff
}
}
None of the properties or class definitions include #objc or #objcMembers
I am trying to create a test that will wait for that async call to set the vibration.status.
I have a test function that seems to work (see below) when I declare the status property as #objc or put #objcMembers on the Vibration class.
func testVibrate() {
let invite: SignalingInviteBody = SignalingInviteBody()
let incomingCall = IncomingCall(invite)
let expectation = XCTNSPredicateExpectation(predicate: NSPredicate(format: "status = 2"), object: incomingCall.vibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [expectation], timeout: 3.0)
}
This test also requires #objc on the enum declaration with Objective-C compatible enum declaration, which I don't want as it would only be for testing.
Except for testing, there is no need to make the status property #objc or the Vibration class as #objcMembers and I would rather not change the base program code to a more inefficient style when I don't need the Objective-C compatibility in the base program.
Is there a way to unit test this in a real, honest to goodness, Swift way?
Preface: this is just some pseudo-code I nailed out quickly in the browser. It'll probably need some polishing before it compiles properly.
I would use a mock and dependency injection:
class MockVibration: Vibration {
let statusChanged: (VibrationStatus) -> Void
init(statusChanged: (VibrationStatus) -> Void) {
self.statusChanged = statusChanged
}
var status: VibrationStatus {
didSet {
statusChanged(status)
}
}
}
I would probably have a protocol, and have Vibration and MockVibration both conform to it, but having MockVibration: Vibration should work well. Once you've defined this mock, you can use it to fulfill an expectation in your test case:
func testVibrate() {
let didVibrate = self.expectation(description: "Started vibrating")
let mockVibration = MockVibration { newStatus in
XCTAssertEqual(newStatus, .vibrating)
didVibrate.fulfill()
}
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [didVibrate], timeout: 3.0)
}
You might even be able to rework this interface so that DispatchQueue.main.async {} happens within the Vibration class as an internal detail. If you do that, the interaction between IncomingCall and Vibration becomes synchronous, so you wouldn't need to use expectations. Your incoming call test would reduce to:
class MockVibration {
// The mock can just have its status set simply/synchronously
var status: VibrationStatus = .off // or some other "initial" value
}
func testVibrate() {
let mockVibration = MockVibration()
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
XCTAssertEqual(mockVibration.status, .vibrating)
}
Of course, then you'd need a separate test that covers Vibration, and ensures that its public APIs cause it to change its internal state using the right dispatch queue or whatever.

Testing multiple methods calling order

I have 3 methods that are related to a specific class which is defined as follows:
class MyClass: NSObject {
func myMethod() {
methodA()
methodB()
methodC()
}
func methodA() {}
func methodB() {}
func methodC() {}
}
I need to test that myMethod has called all 3 methods by the order they are implemented: methodA then methodB then methodC
to be tested with XCode Unit Tests, regardless of the implementation of these methods, I have created a subclass in the test case that looks like the following:
class ChildClass: MyClass {
var method1CallingDate: Date?
var method2CallingDate: Date?
var method3CallingDate: Date?
override func methodA() {
super.methodA()
method1CallingDate = Date()
}
override func methodB() {
super.methodB()
method2CallingDate = Date()
}
override func methodC() {
super.methodC()
method3CallingDate = Date()
}
}
Now in the test method, I start by calling those 3 methods, then I assert that all three dates are not nil first, then compare them like this:
XCTAssertLessThan(method1CallingDate, method2CallingDate)
XCTAssertLessThan(method2CallingDate, method3CallingDate)
The problem I ran into was that the test sometimes succeeds and sometimes fails, i guess due to Date object is (randomly) the same between 2 of the method calls.
Is there a better way to test the order of calling multiple methods ?
p.s. this is easily done in the Android SDK org.mockito.Mockito.inOrder
First, make a mock object that records the order. No dates, no strings. Just an enumeration.
class MockMyClass: MyClass {
enum invocation {
case methodA
case methodB
case methodC
}
private var invocations: [invocation] = []
override func methodA() {
invocations.append(.methodA)
}
override func methodB() {
invocations.append(.methodB)
}
override func methodC() {
invocations.append(.methodC)
}
func verify(expectedInvocations: [invocation], file: StaticString = #file, line: UInt = #line) {
if invocations != expectedInvocations {
XCTFail("Expected \(expectedInvocations), but got \(invocations)", file: file, line: line)
}
}
}
Then in the test:
mock.verify(expectedInvocations: [.methodA, .methodB, .methodC])
No async waiting. Simple to call. Clear failure messages.
You could do something like this using a String to keep track of the order:
class ChildClass: MyClass {
var order = ""
override func methodA() {
super.methodA()
order = String((order + "A").suffix(3))
}
override func methodB() {
super.methodB()
order = String((order + "B").suffix(3))
}
override func methodC() {
super.methodC()
order = String((order + "C").suffix(3))
}
}
Then, just check that order is "ABC".
Or, if it is valid to call B multiple times between A and C:
class ChildClass: MyClass {
var order = ""
override func methodA() {
super.methodA()
order = order.replacingOccurrences(of: "A", with: "") + "A"
}
override func methodB() {
super.methodB()
order = order.replacingOccurrences(of: "B", with: "") + "B"
}
override func methodC() {
super.methodC()
order = order.replacingOccurrences(of: "C", with: "") + "C"
}
}
Example:
let c = ChildClass()
c.methodA()
c.methodB()
c.methodB()
c.methodC()
print(c.order)
ABC
I've become a fan of using XCTestExpectation for this kind of thing. Here's an option.
class MyTestableClass: MyClass {
var methodAHandler: (() -> Void)?
// ...
override func methodA() {
methodAHandler?()
super.methodA()
}
And then in your test case
let expA = XCTestExpectation(description: "Method A Called")
let expB = ...
let expo = ...
objectUnderTest.methodAHandler = { expA.fulfill() }
/// ...
objectUnderTest.myMethod()
// ensure you use the enforceOrder param, which is optional
wait(for: [expA, expB, expC], timeout: 1.0, enforceOrder: true)
XCTestExpectation is made more for async testing, so the wait is slightly funny. But, it does do what you need, and would keep working even if eventually the internals of myMethod become asynchronous for some reason.
While I haven't used it myself, you also might want to check out Cuckoo. It's a mocking framework for Swift.
You're not asking the right question here. From a unit testing point of view you should not know/care that the tested method calls other methods, or even if other methods exist.
Unit tests should validate some observable result of the tested method. Anything that happens inside the tested method is irrelevant in the context of a unit test.
That's because unit tests should validate that the unit behaves as expected, i.e. they should validate against the specifications, not against the implementation.
Let's consider a simple example, unit testing a isPrime(n) function. Unless you're doing performance testing, you only care if the function returns the appropriate result for a couple of numbers. You don't care if the function checks all possible divisors, or if it uses a database of all known prime numbers, or if delegates the prime check to some 3rd party library/service.
The situation is not much different from yours. The fact that the three methods are called in a certain order needs to be validate via the external interface of the tested unit. For example if the three methods make API calls, then mock the API client and expect it to be requested three times, and with the expected URL/payload. If calling the three methods don't result in any noticeable changes, then there's not much you can test from the start, so again the fact that three methods are called in a certain order become irrelevant.
Unit testing is about validating the result of the execution of that unit, not anything more. Now, in an imperative programming language, the input->output functions are a minority, however this doesn't mean that we can't indirectly test if the function behaves as expected. You can use mocks, or validate some properties of the object after the function executes. Again, if there are no ways of externally checking the order of methods, then you have no specs to validate against.

Converting Older KVO to Swift 4

I'm trying to convert some old WWDC swift code to Swift 4. I think that I have everything done, except for this last bit that does some KVO. This has been pretty difficult to narrow it down to this last bit because everything appears to function like the example code - but these KVO methods do not get called in Swift 4. I found that out here: Open Radar Bug
What would be the Swift 4 way to represent the following?
// use the KVO mechanism to indicate that changes to "state" affect other properties as well
class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> {
return ["state" as NSObject]
}
class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
return ["state" as NSObject]
}
class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
return ["state" as NSObject]
}
And here are the variable definitions from the example:
override var isReady: Bool {
switch state {
case .initialized:
// If the operation has been cancelled, "isReady" should return true
return isCancelled
case .pending:
// If the operation has been cancelled, "isReady" should return true
guard !isCancelled else {
return true
}
// If super isReady, conditions can be evaluated
if super.isReady {
evaluateConditions()
}
// Until conditions have been evaluated, "isReady" returns false
return false
case .ready:
return super.isReady || isCancelled
default:
return false
}
}
override var isExecuting: Bool {
return state == .executing
}
override var isFinished: Bool {
return state == .finished
}
If more code is needed, please let me know.
If this is a duplicate question, please link to the duplicate here. I've been unable to find a solution.
The keyPathsForValuesAffecting… members can be properties instead of methods.
They must be declared #objc because the KVO system accesses the properties using the Objective-C runtime.
The properties should have type Set<String>.
If you use the #keyPath directive, the compiler can tell you when you've used an invalid key path (for example because of a spelling error or a change to the property name).
Thus:
#objc class var keyPathsForValuesAffectingIsReady: Set<String> {
return [#keyPath(state)]
}
#objc class var keyPathsForValuesAffectingIsExecuting: Set<String> {
return [#keyPath(state)]
}
#objc class var keyPathsForValuesAffectingIsFinished: Set<String> {
return [#keyPath(state)]
}
You also need to make sure your state property is declared #objc dynamic.
The main problem is that KVO is built using Objective-C, and it uses the Objective-C runtime to detect the existence of the keyPathsForValuesAffecting methods. In Swift 4, methods are no longer exposed to Objective-C by default if you don't include an #objc annotation on them. So, in a nutshell, adding the #objc annotation will probably fix your problem.
Another thing that I do—not strictly necessary, but it makes the code look a bit nicer—is to declare these as static constants. The #objc will cause these to get exposed to Objective-C as class methods, so it all works, and it's slightly cleaner. I like to put private on them, too, since these will never get called by Swift code, and there's no point cluttering your class's internal and/or public interface.
You also need to make sure that your state property is KVO-compliant, and sending the notifications when it is changed. You can either do this by making the property dynamic, which will cause the KVO system to automatically generate the notification calls for you, or you can manually call willChangeValue(for:) and didChangeValue(for:) (or the string-based versions, willChangeValue(forKey:) and didChangeValue(forKey:)) in your willSet and didSet handlers for the property.
Finally, don't use raw string key paths in Swift if you can avoid it. The #keyPath() mechanism is the preferred way to get string-based key paths (and for uses other than these legacy Objective-C methods that need to take strings, you should use the new KeyPath type which is better still). If your state property is not an Objective-C-compatible type, though, you're stuck with the old string key paths (in which case you'll fire the notifications in your willSet and didSet as described in the previous paragraph). Alternatively, you can create a dummy Any-typed object that mirrors your state property, purely for KVO purposes.
So, something like this:
#objc private static let keyPathsForValuesAffectingIsReady: Set<String> = [
#keyPath(state)
]
Now, the state property. If it's an Objective-C-compatible type, it's easy:
#objc dynamic var state: ...
Or, if it's not:
#objc var state: SomeNonObjCThing {
willSet { self.willChangeValue(forKey: "state") }
didSet { self.didChangeValue(forKey: "state") }
}
OR:
#objc private var _stateKVO: Any { return self.state }
var state: SomeNonObjCThing {
willSet { self.willChangeValue(for: \.stateKVO) }
didSet { self.didChangeValue(for: \.stateKVO) }
}
// you can now use #keyPath(_stateKVO) in keyPathsForValuesAffecting...
(NS)Operation relies heavily on NSObject-KVO
The closest Swift syntax is
#objc private class func keyPathsForValuesAffectingIsReady() -> Set<String> {
return [#keyPath(state)]
}
#objc private class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
return [#keyPath(state)]
}
#objc private class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
return [#keyPath(state)]
}
Side note: You might need to make state thread-safe.

Swift function can be called only once

What is the simplest way to write a piece of code that can be executed only once?
I know a way but has a problem.
first, I write a Boolean variable that has negative value but can be set to positive and cannot change after that
var hasTheFunctionCalled : Bool = false {
didSet{
hasTheFunctionCalled = true
}
}
and then write the function and the code inside it:
func theFunction(){
if !hasTheFunctionCalled{
//do the thing
}
hasTheFunctionCalled = true
}
but the problem is that the variable can be changed from somewhere else in the scope and this solution doesn't really look so simple and concrete.
A simple solution is to take advantage of lazy variables in the following way:
// Declare your "once-only" closure like this
private lazy var myFunction: Void = {
// Do something once
}()
...
// Then to execute it, just call
_ = myFunction
This ensures that the code inside the myFunction closure is only executed the first time that the program runs _ = myFunction
Edit: Another approach is to use so called "dispatch once tokens". This comes from Objective-C and was available in Swift until Swift 3. It is still possible to make it work, however you will need to add a little bit of custom code. You can find more information on this post -> dispatch_once after the Swift 3 GCD API changes
Edit2: Should be _ = myFunction and not _ = myFunction(), as JohnMontgomery pointed out.
You might use a static bool inside a struct nested into the function itself doing so:
func theFunction(){
struct Holder { static var called = false }
if !Holder.called {
Holder.called = true
//do the thing
}
}
One possible technique is to put the code into the initializer of a static type property, which is guaranteed to be lazily initialized only once (even when accessed across multiple threads simultaneously):
func theFunction() {
struct Once {
static let once = Once()
init() {
print("This should be executed only once during the lifetime of the program")
}
}
_ = Once.once
}
(Compare Singleton in the "Using Swift with Cocoa and Objective-C" reference.)
Example:
print("Call #1")
theFunction()
print("Call #2")
theFunction()
print("Done")
Output:
Call #1
This should be executed only once during the lifetime of the program
Call #2
Done
You can do smth like:
class Once {
var already: Bool = false
func run(#noescape block: () -> Void) {
guard !already else { return }
block()
already = true
}
}
and than use it like
class ViewController: UIViewController {
let once = Once()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
once.run {
cameraMan.setup()
}
}
}
ref: https://dev.to/onmyway133/how-to-run-action-once-in-swift-3k7o
Depending on what you are doing inside your method : you may check if the end result has already been accomplished :
e.g. if you instantiate a class, check if it is different from nil
You can also use UserDefaults, and the knowledge that the default UserDefault Bool is false:
if !UserDefaults.standard.bool(forKey: "ExecuteOnce") {
func()
UserDefaults.standard.set(true, forKey: "ExecuteOnce")
}
This code will execute exactly once.

Variable of type Self in static context

I have to modify an existing static method with return type Self.
I am using Self as it is required to work for subclasses of A as well. As the modification potentially needs a dispatch sync block to create the data for the returnee, I have to introduce a local variable of type Self.
Error:
'Self' is only available in a protocol or as the result of a method in a class;
class A {
//...
}
class B:A {
//...
}
extension A {
static private func foo() -> Self {
var myVar: Self? //Error: 'Self' is only available in a protocol or as the result of a method in a class;
// Get data for myVar, potentially in a dispatch sync block on another queue
guard let safeVar = myVar else {
return someGarbagr
}
return myVar
}
}
Intended usage:
func someFunctionSomewhere() {
let instanceOfB = B.foo()
// use instanceOfB
}
I have tried all I can think of already:
type(of:Self)
Self.Type
...
I would like to avoid modifying it to a generic method for several reasons. The main reason is that we would have to mention the type explicitly to make a generic version be able to refer the return type:
let instanceOfB: B = B.foo()