Trikalabs
  • Home
  • Best online TDD videos
  • Book Suggestions
  • About Me
  • Contact
Trikalabs
No Result
View All Result

Test static method on collaborator

by fragi
May 13, 2021
in Unit Testing
Share on FacebookShare on Twitter

One of the main issues we are facing when we try to make an old piece of code testable is dealing with static methods. Likely the are ways to make our code testable. One of them we will explain in this article.
Let’s start with a simple example to demonstrate the issue.

We have a struct named “AModel” that has a method to return the a greeting. In this method there is a call to a static method.

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Foundation
 
struct AModel {
    private let name: String
    
    func getGreeting() -> String {
        return ACollaborator.createGreeting(name)
    }
}
 
import Foundation
 
class ACollaborator {
    
    static func createGreeting(_ name: String) -> String {
        return "Good morning \(name)"
    }
}
 

How can we write a test for this?

There are two big issues with the above code. Firstly the AModel has an implicit dependency on ACollaborator. Secondly there is a call on static method that we need to control.

Lets’ address first the implicit dependency issue, by using constructor dependency injection and an interface.

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
 
struct AModel {
    private let name: String
    private let aCollaborator: ACollaboratorProtocol
 
    init(name: String, aCollaborator: ACollaboratorProtocol) {
        self.name = name
        self.aCollaborator = aCollaborator
    }
    func getGreeting() -> String {
        return type(of: aCollaborator).createGreeting(name)
    }
}
 
import Foundation
 
protocol ACollaboratorProtocol {
    static func createGreeting(_ name: String) -> String
}
 
class ACollaborator: ACollaboratorProtocol {
    
    static func createGreeting(_ name: String) -> String {
        return "Good morning \(name)"
    }
}

And now let’s get rid of the static method.

In this example is easy to just convert the static method to instance method

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
 
struct AModel {
    private let name: String
    private let aCollaborator: ACollaboratorProtocol
 
    init(name: String, aCollaborator: ACollaboratorProtocol) {
        self.name = name
        self.aCollaborator = aCollaborator
    }
    func getGreeting() -> String {
        return aCollaborator.createGreeting(name)
    }
}
 
import Foundation
 
protocol ACollaboratorProtocol {
    func createGreeting(_ name: String) -> String
}
 
class ACollaborator {
    
    func createGreeting(_ name: String) -> String {
        return "Good morning \(name)"
    }
}

But let assume that is difficult to convert the static method to instance method. Then we can use the following approach

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import Foundation
 
struct AModel {
    private let name: String
    private let aCollaborator: ACollaboratorProtocol
 
    init(name: String, aCollaborator: ACollaboratorProtocol) {
        self.name = name
        self.aCollaborator = aCollaborator
    }
    func getGreeting() -> String {
        return aCollaborator.createGreeting(name)
    }
}
 
import Foundation
 
protocol ACollaboratorProtocol {
    func createGreeting(_ name: String) -> String
}
 
class TesatbleACollaborator: ACollaboratorProtocol {
    func createGreeting(_ name: String) -> String {
        ACollaborator.createGreeting(name)
    }
}
 
class ACollaborator {
 
    static func createGreeting(_ name: String) -> String {
        return "Good morning \(name)"
    }
}

And now we can write our tests:

Swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import XCTest
@testable import StaticMethodOnCollaborator
 
class AModelTests: XCTestCase {
 
    func test_getGreeting_returns_correct_string() {
        let sut = AModel(name: "Fragi", aCollaborator: TesatbleACollaborator())
        
        let greeting = sut.getGreeting()
        
        XCTAssertEqual("Good morning Fragi", greeting)
    }
    
    func test_getGreeting_callsACollaborator() {
        let mockTestableCollaborator = MockTestableCollaborator()
        let sut = AModel(name: "Fragi", aCollaborator: mockTestableCollaborator)
        
        _ = sut.getGreeting()
        
        XCTAssertEqual(1, mockTestableCollaborator.didCallCreateGreeting.count)
        XCTAssertEqual("Fragi", mockTestableCollaborator.didCallCreateGreeting.first)
    }
    
    private class MockTestableCollaborator: ACollaboratorProtocol {
        
        private(set) var didCallCreateGreeting: [String] = []
        func createGreeting(_ name: String) -> String {
            didCallCreateGreeting.append(name)
            return ""
        }
    }
}

 

 

fragi

fragi

Related Posts

What is TDD?
Unit Testing

Write better Unit tests with XCTUnwrap

January 27, 2022

Testing optionals can require boilerplate code to unwrap it. It can also affect the readability of the test. Let's look...

Dependency injection – UIViewController in Storyboard (iOS 13)
Unit Testing

Dependency injection – UIViewController in Storyboard (iOS 13)

May 13, 2021

Finally Apple (iOS 13 +) has added constructor dependency injection to Storyboards! No excuses anymore for not using DI properly...

Unit testing code with DispatchQueue
Unit Testing

Unit testing code with DispatchQueue

May 13, 2021

One way of testing code with DispatchQueue is by using expectations (https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations/testing_asynchronous_operations_with_expectations) but in most of the cases it's more...

Unit testing UIViewController LifeCycle
Unit Testing

Unit testing UIViewController LifeCycle

May 13, 2021

Unit testing UIViewController life cycle events are not a straightforward process. Let's have a look on the following view controller:...

Dependency Injection – UIViewController with a Nib file
Unit Testing

Dependency Injection – UIViewController with a Nib file

May 13, 2021

UIViewControllers are among the most used UIKit objects. As the name implies UIViewControllers are controller objects and their purpose should...

Unit Test Post Notification
Unit Testing

Unit Test Post Notification

July 19, 2019

In this article, we will see how we can test code that post notifications. Let's see our example code: ...

Next Post
Large Content Viewer

Large Content Viewer

Large Content Viewer

SwiftUI - Accessibility for Images

What is TDD?

FizzBuzzThrow - step by step TDD

  • Advertise
  • Privacy & Policy
  • Contact

© 2019 Trikalabs Ltd. All rights reserved.

No Result
View All Result
  • Home
  • About Me
  • A curated list with the best free online TDD videos
  • Book Suggestions
  • Pinner Code Club

© 2019 Trikalabs Ltd. All rights reserved.