How to use Resolver when writing unit tests - swift

I hope all is well and you are enjoying WWDC 2020 online. I have a question. I am using the pod 'Resolver' for dependency injection. It is working great. Until I write my unit tests to test my view models. I get this crash:
public final func resolve<Service>(_ type: Service.Type = Service.self, name: String? = nil, args: Any? = nil) -> Service {
if let registration = lookup(type, name: name ?? NONAME),
let service = registration.scope.resolve(resolver: self, registration: registration, args: args) {
return service
}
print("RESOLVER: '\(Service.self):\(name ?? "")' not resolved. To disambiguate optionals use resover.optional().")
fatalError()
}
The test:
import XCTest
import Resolver
class QuizRowViewModelModelTests: XCTestCase {
#Published var quizRepository: QuizRepository = Resolver.resolve()
I have debugged the section where it sets QuizRepository and this works fine.
register { TestDataQuizRepository() as QuizRepository }.scope(application)
Do you happen to have any idea as to why this would happen?
Thanks a million

I'd registered the Repository inside of the setUp function.
override func setUp() {
Resolver.register {
GetAllGuideShopsRepositoryMock(withSuccess: true)
}.implements(GetAllGuideShopsRepository.self)
}
Then, I'd resolved it inside of my test.
sut.getAllGuideShopsRepository = Resolver.resolve(GetAllGuideShopsRepositoryMock.self)
If you want to see more details, please check the Test Class on github.com

Related

Unable to cast to protocol from framework during test cases

So I have a class that comes from one of our internal frameworks. It is defined as follows:
// This lives within a framework
class ExternalClass: ExternalClassProtocol {
// implementation here
}
// This lives within my test target
class MockExternalClass: ExternalClassProtocol {
// Mock implementation here
}
// This lives within the same external frame work as ExternalClass
protocol ExternalClassProtocol: AnyObject {
// Protocol methods here
}
During my test cases, if I try to cast MockExternalClass as? ExternalClassProtocol, the test case crashes.
However, during live app runtime, there is no problem casting ExternalClass as? ExternalClassProtocol.
Is this an issue with trying to implement a protocol from an external module? Is there a way around this?
The class is being accessed through dependency injection (see below dependency injection implementation). The crash occurs on the resolve function.
If you actually debug to this point, you can see that the mock dependency IS in my dependency root (the services array below).
So I know its not failing to cast due to the dependency being missing.
#propertyWrapper
struct Injected<Value> {
var key: String
var wrappedValue: Value {
get { return Dependency.root.resolve(key: self.key) }
set { Dependency.root.add(key: self.key, newValue) }
}
init(key: String) {
self.key = key
}
}
class Dependency {
static let root = Dependency()
var services: [String : Any] = [:]
func add<T>(key: String, _ service: T) {
services[key] = service
}
func resolve<T>(key: String) -> T {
guard let component: T = services[key] as? T else {
// The test crashes here. It works fine on other mocks that are internal to the project
fatalError("Dependency '\(T.self)' not resolved!")
}
return component
}
func clearDependencies() {
self.services.removeAll()
}
private init() {}
}
In my test case:
#testable import MyProject
import ExternalDependency
class TestCase: XCTestCase {
private var subject: ClassWithService!
private var mockInternalClass: MockInternalClassProtocol!
private var mockExternalClass: MockInternallClassProtocol!
func setUp() {
mockExternalClass = MockExternalClass() // This one crashes when trying to cast to its parent protocol
mockInternalClass = MockInternalClass() // This one does not crash when casting to parent protocol.
Dependency.root.add(key: "internal_class", mockInternalClass)
Dependency.root.add(key: "external_class", mockExternalClass)
}
}
Some things I've tried:
Adding AnyObject to the protocol (this fixed a similar issue for internally defined classes that I mock).
changing mockExternalClass type to be the protocol
changing mockExternalClass type to be the implementation
Aside from one protocol being defined in one of our pods, there is no difference between the external protocol and the one we have defined in our own project.
One thing I have noticed is that the cast does not fail if you set a break point inside one of my test case functions. But if you try the same cast within the Dependency.resolve function it crashes. Which leads me to believe there is an issue with the generics.
Any ideas?

Dependency injection: static methods and parameters

I am attempting to add unit tests to a Swift program that is layered on an Objective-C library. My main problem at the moment is finding a way to inject a dependency that is created using a parameterized static factory method.
As an example, the following code is functional but rather test-resistant:
class Processor {
var service: RegistrationService?
func register(user: String, pass: String) {
let configuration = Configuration(user: user, pass: pass)
service = RegistrationServiceProvider.registrationService(configuration: configuration)
// Do various things with the 'service'
}
}
Note that RegistrationServiceProvider, RegistrationService, and Configuration are all from the Objective-C library.
What I'd like to be able to do is to provide the RegistrationService that's created in this code as the default and replace it with my own mock when testing. Without the Configuration object that would be fairly simple using something like http://www.danielhall.io/swift-y-dependency-injection-part-two.
(I realize that I could/should push the Configuration construction to the caller, but that doesn't solve the problem of how to supply it to a default service.)
Suggestions and references are welcome.
You can create a mock of both RegistrationService and RegistrationServiceProvider and inject them in the test, using the standard Type as the default type in the normal call, like the code below (it includes example versions of the classes that you use and some printouts to see what is called):
class Configuration {
let user: String
let pass: String
init(user: String, pass: String) {
self.user = user
self.pass = pass
}
}
class RegistrationService {
let configuration: Configuration
init(configuration: Configuration) {
self.configuration = configuration
}
}
class RegistrationServiceProvider {
class func registrationService(configuration: Configuration) -> RegistrationService {
print("Provider instantiated service")
return RegistrationService(configuration: configuration)
}
}
class Processor {
var service: RegistrationService?
func register(user: String, pass: String, serviceProvider: RegistrationServiceProvider.Type = RegistrationServiceProvider.self) {
let configuration = Configuration(user: user, pass: pass)
service = serviceProvider.registrationService(configuration: configuration)
// Do various things with the 'service'
}
}
class MockProvider: RegistrationServiceProvider {
override class func registrationService(configuration: Configuration) -> RegistrationService {
print("Mock provider instantiated mock service")
return MockService(configuration: configuration)
}
}
class MockService: RegistrationService {
override init(configuration: Configuration) {
super.init(configuration: configuration)
print("Mock service initialized")
}
}
let processor = Processor()
processor.register(user: "userName", pass: "myPassword") // Provider instantiated service
processor.register(user: "userName", pass: "myPassword", serviceProvider: MockProvider.self) // Mock provider instantiated mock service
I ended up with a slightly different implementation than the accepted answer above using a protocol instead of inheritance. I don't believe it is a better implementation. Just a personal preference. It does require extra code to define the protocol.
protocol ServiceProvidable {
func registrationService(configuration: Configuration) -> RegistrationService
}
That would change the parameters of the register function to...
func register(user: String, pass: String, serviceProvider: ServiceProvidable.Type = RegistrationServiceProvider.self)
and the provider would conform to the protocol...
class RegistrationServiceProvider: ServiceProvidable
Now your mock providers could simply adhere to the protocol and implement the required function without an override. You also get the added benefit of letting Xcode stub out the function. Not a big deal but a small convenience.
Just a different perspective from a protocol oriented programming style.

Swift MVVM testing strategy and code coverage questions

I've run into an issue when generating code coverage with Xcode for view models in an MVVM environment.
Our basic setup is the view controller makes requests to the view model, which in turn calls methods on a data manager that talks to web services.
I came up with what I thought was a reasonably elegant way to test the view models by creating a fake data manager that subclasses the actual data manager and overrides the function called by the VM.
The problem is that for this to work, the VM must be part of the app target and the test target. An apparent side effect of this is that code coverage is not generated for items belonging to two or more targets, even though the unit tests pass. Code coverage is enabled in the project.
Here is a excerpted view model:
import Foundation
class BoosViewModel: BaseViewModel {
convenience override init() {
self.init(dataManager: BoosDataManager(), andModel: nil)
}
func getUnlinkedBoos(_ cardType: CardType) {
(dataManager as! BoosDataManager).getUnlinkedBoos(cardType) { result, error in
...stuff happens here...
}
}
}
... and the data manager
class BoosDataManager: DataManager {
static let SharedInstance: BoosDataManager = {
var manager = BoosDataManager()
return manager
}()
func getUnlinkedBoos(_ cardType: CardType = .loyalty, completion: #escaping ((_ result: BoosModel?, _ error: NSError?) -> Void)) {
...stuff happens here...
}
}
...and the test
class BoosViewModelTests: XCTestCase {
func testGetUnlinkedBoosHappyPath() {
class FauxDataManager: BoosDataManager {
override func getUnlinkedBoos(_ cardType: CardType = .loyalty, completion: #escaping ((_ result: BoosModel?, _ error: NSError?) -> Void)) {
...stuff happens here...
}
}
let viewModel = BoosViewModel()
let dataManager = FauxDataManager()
viewModel.dataManager = dataManager
viewModel.getUnlinkedBoos(.loyalty)
XCTAssertTrue(testObserver.updated)
XCTAssertEqual(testObserver.newViewModel.getBoos().count, 1)
}
}
As I noted earlier the unit tests in this scenario complete successfully, but unit coverage does not get generated.
I have older tests where I actually created an external fake data manager class that was used by the test, the class under test is not part of the test target, and coverage works fine.
The drawback to that is that I have to create multiple data managers to handle specific cases for its returns. If I can't encapsulate the classes, I would need to create a bunch of swift data managers, one for each scenario.
That's why I came up with the internal class.
Now, the problem comes in if I remove the view model under test from the testing target. After doing this, I add #testable import BoosApp to the unit test so that the view model under test can be resolved. When I do this, I get the following error:
Could not cast value of type 'BoosTests.BoosViewModelTests.(testGetUnlinkedBoosHappyPath () -> ()).(FauxDataManager #1)' (0x11f673d18) to 'Boos.BoosDataManager' (0x10444b128).
Aug 30 20:43:01 Pay[19025] : Could not cast value of type 'BoosTests.BoosViewModelTests.(testGetUnlinkedBoosHappyPath () -> ()).(FauxDataManager #1)' (0x11f673d18) to 'Boos.BoosDataManager' (0x10444b128).
I'm not sure what I'm missing. Is there a way to make this scenario work, or am I stuck creating multiple data managers outside of the test code?
Ultimately, I figured out the main issue was that the view model and data manager had somehow gotten added to the test target. After removing them from the test target I was able to make a couple of minor changes and everything is running fine. FYI.

XCTest Single Asynchronous SetUp With Semaphores

I am working on testing an API through Alamofire. I need to make a single call to the server to prepare it for the integration test. Once that is done, I am ready to start running tests.
The usual override setUp() is run for every test, so I do not want to do that.
I have therefore chosen to override the class setUp() as described here: https://developer.apple.com/reference/xctest/xctestcase
That's all well and good, but now, I no longer can use the standard waitForExpectations. (In the class override setUp()) I get several compiler errors that tell me that I am no longer calling the same waitForExpectations because I am in a class method, not a test case.
To try to get around this, I wanted to use a semaphore like so:
class ServiceLayerTests: XCTestCase {
static var apiService: APIService = APIService()
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: ["http://localhost:3000/": .disableEvaluation]))
static var companyManger: UserWebResource?
static var companyManagerID = -1
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
beginIntegrationTests()
}
class func beginIntegrationTests() {
var semaphore = DispatchSemaphore(value: 0)
apiService.beginIntegrationTests(completion: {resource, error in
if let resource = resource {
if let manager = resource as? UserWebResource {
companyManger = manager
companyManagerID = manager.id
semaphore.signal()
}
}
})
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
}
}
This does not work. Under the hood, there is an alamo fire call to the server and it responds with the user to use for the integration tests. I do see the server spinning, so I know that the actual communication is happening, but I never get into the completion closure.
I suspect I am not understanding how Swift does semaphores and that I have created a deadlock somehow. If somebody has a better solution, I'd be more than happy to hear it.
I get several compiler errors that tell me that I am no longer calling
the same waitForExpectations because I am in a class method, not a
test case
That makes sense. What you probably want is to refactor so that you are in a test case:
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
}
func testIntegrationTests() {
let urlExpectation = expectation(description: "INTEGRATION TEST")
apiService.beginIntegrationTests(completion: {resource, error in
// ...
urlExpectation.fulfill()
})
// not sure what an acceptable timeout would be, I chose this at random
waitForExpectations(timeout: 25) { error in
if let error = error {
print("Error: \(error.localizedDescription)")
}
}
}
One of the best resources with some good test examples can be found here: http://nshipster.com/xctestcase/
You can create the expectation as a lazy var that executes your one-time set up and fulfills on completion.
At the beginning of your per-test setUp() function you can wait for that expectation.
None of your tests will run until it is fulfilled, and the initial setup will run only once.
class WaitForSetup_Tests: XCTestCase {
lazy var initialSetupFinished: XCTestExpectation = {
let initialSetupFinished = expectation(description: "initial setup finished")
initialSetupTask() { // your setup task that executes this closure on completion
initialSetupFinished.fulfill()
return
}
return initialSetupFinished
}()
override func setUp() {
wait(for: [initialSignOutFinished], timeout: 2.0)
// do your per-test setup here
}
}
Note: This solution avoids using the override class function setUp() class method, because I couldn't figure out how to use the expectations except for in an instance.

Variable declaration inside of class in Swift

I'm new in swift automated testing with XCTest framework.
In my work I faced with correct arrangement some parts of code inside of test
Please explain the difference between variable declaration in the beginning of the class and in the certain function. Which way is preferred?
Var.1
class someClass: XCTestCase {
let app = XCUIApplication()
func someFunc {
print (app.debugdescription)
}
}
Var. 2
class someClass: XCTestCase {
func someFunc {
let app = XCUIApplication()
print (app.debugdescription)
}
}
Var. 1 gives you the ability to configure your XCUIApplication in setup() and use it in the tests. Like this:
class someClass: XCTestCase {
var app: XCUIApplication!
func setUp {
app = XCUIApplication()
app.launchArguments.append("launchArgument")
setupSnapshot(app)
app.launch()
}
func test1 {
XCTAssertNotNil(app)
}
func test2 {
XCTAssertNotNil(app)
}
}
With Var. 2 you can setup your app differently in each test. Like this:
class someClass: XCTestCase {
func test1 {
let app = XCUIApplication()
app.launchArguments.append("withTestUser")
app.launch()
XCTAssertNotNil(app)
}
func test2 {
let app = XCUIApplication()
app.launchArguments.append("withNoData")
app.launch()
XCTAssertNotNil(app)
}
}
Both ways are possible but var 2 is preferred. For var 1 solution consider the following scenario:
class State {
var val: Int = 0
}
class TestClass: XCTestCase {
let state = State()
func test1() {
state.val = 5
XCTAssertEqual(5, state.val)
}
func test2() {
XCTAssertEqual(0, state.val)
}
}
This way result depends on which test will run first. If test2 will be the first then both tests will succeed. Other way, second test will fail.
For var 2 solution
class TestClass: XCTestCase {
func test1() {
let state = State()
state.val = 5
XCTAssertEqual(5, state.val)
}
func test2() {
let state = State()
XCTAssertEqual(0, state.val)
}
}
both tests succeeds no matter what test will run firstly. This makes var 2 solution preferred in most scenarios.
There is a case when var 1 is more convenient than var 2. It is when SUT have many dependencies and you have to copy and paste creation code for them in each test. Then you can declare dependencies as a class variables, but you must use test setUp (and possibly a tearDown) to ensure that their states is refreshed before each test
class TestClass: XCTestCase {
var dep1: Dependency1!
var dep2: Dependency2!
var sut: SUT!
func setUp() {
dep1 = Dependency1()
dep2 = Dependency2()
sut = SUT(dep1, dep2)
}
}
Variable declaration as Class level or the Function level is same as in other programming languages, so it would be great if you understand their basic principle.
Class level variable means that this variable can be accessed throughout the class, that means it can be used in any function of the same class. It also means that if you make the class as public, and that variable as public, then once the class is initialized it could be accessed within your program in other classes. Usually they could create memory issues if you don't manage them properly as they stay in the memory when the class itself is within the memory.
Function level variable can only be accessed within that specific function and not outside that function whether it's the same class or a different class, they have no reference outside their function. Usually they don't create memory issues in your whole program as these variables leave the memory when that function is completely executed.