Denis Balko
Denis Balko

Reputation: 1596

Swift dependency injection by passing class as a function parameter to call a class method

I may be approaching this the wrong way, but I would like to introduce dependency injection for unit testing purposes, and in order to do so I would need to pass a class as a parameter. In somewhat of a pseudo code it should look like this:

protocol LocalSavingProtocol {
    static func saveDataLocally()
}

class ProductionClass: LocalSavingProtocol {
    static func saveDataLocally() {
        print("actually save data")
    }
}

class TestClass: LocalSavingProtocol {
    static func saveDataLocally() {
        print("test saving data")
    }
}

func saveEverywhere(injecting localSavingClass: LocalSavingProtocol = ProductionClass) {
    localSavingClass.saveDataLocally()
}

//production code
saveEverywhere()

//test
saveEverywhere(injecting: TestClass)

Obviously this does not work, and as much as I tried playing around with passing .Type or .self, changing the saveEverywhere function to

saveEverywhere<T: LocalSavingProtocol>(injecting localSavingClass: T)

and so on, nothing has worked.

The goal is to have a system such that:

  1. I can inject a class when testing the code
  2. I don't have to inject the class in production code (i.e. there is a default)
  3. I can call a class method on the injected class

Upvotes: 2

Views: 1420

Answers (2)

Gi0R
Gi0R

Reputation: 1457

protocol LocalSavingProtocol {
    static func saveDataLocally()
}

class ProductionClass: LocalSavingProtocol {
    class func saveDataLocally() {
        print("actually save data")
    }
}

class TestClass: LocalSavingProtocol {
    class func saveDataLocally() {
        print("test saving data")
    }
}

func saveEverywhere(injecting localSavingClass: LocalSavingProtocol.Type = ProductionClass.self) {
    localSavingClass.saveDataLocally()
}

//production code
saveEverywhere()

//test
saveEverywhere(injecting: TestClass.self)

Upvotes: 4

tktsubota
tktsubota

Reputation: 9411

Dependency injection does not involve passing in a class, it injects an object that something else can depend on, hence the word dependency. The reason this is useful for unit testing purposes is that you can encapsulate code into that separate object.

You are trying to access an instance method from a type. Dependency injection does not require a class type to be passed in. Pass in an object instead:

saveEverywhere(injecting: TestClass())

For the default value, use an object as well:

func saveEverywhere(injecting localSavingClass: LocalSavingProtocol = ProductionClass()) {

Note the () in both examples.

Upvotes: -1

Related Questions