jmjauer
(Johannes Auer)
1
Hi,
currently there is no clean way to implement dependency injection without modifying the code that should be tested. This is not ideal. How about a language feature that would allow rebinding types for a small context:
import Foundation
struct FileReader {
func readInfo() throws -> Data {
let infoURL = URL(fileURLWithPath: "/Info")
return try Data(contentsOf: infoURL)
}
}
import XCTest
class FileReaderTests: XCTestCase {
func testReadInfo() {
// This call would allow to rebind types with mocks and execute a closure with those rebound types
Inject(Data: DataMock) {
let info = try FileReader().readInfo()
// asserts
}
}
}
struct DataMock {
init(contentsOf url: URL, options: Data.ReadingOptions = []) {
// do whatever you need to do to test your stuff
}
}
So it looks like you're proposing Inject(TypeToReplace: ReplacementType, () -> Void) where the given closure is executed in a context where references to TypeToReplace are redirected to ReplacementType.
I have no idea how feasible that is to implement, but I'm not sure using the type name as a parameter label is the best approach. I would suggest something more like inject(replacing: Type1, with: Type2) {···}
jmjauer
(Johannes Auer)
3
So it looks like you're proposing Inject(TypeToReplace: ReplacementType, () -> Void) where the given closure is executed in a context where references to TypeToReplace are redirected to ReplacementType .
Yes exactly.
I have no idea how feasible that is to implement, but I'm not sure using the type name as a parameter label is the best approach. I would suggest something more like inject(replacing: Type1, with: Type2) {···}
I agree, my proposed syntax was just a quick way to show what I mean. Since it would be necessary to rebind multiple types in a single context the syntax would be more like inject(_ replacementList: [(replacing: Type1, with: Type2)]) {···}
I have seen this being done by providing a protocol with a single implementation (meaning the optimizer can handle it) but changing which implementation is compiled in upon whether or not one is testing (using a -Dflag or the like. I am not saying it is the best way to do it, but it should work ok and not require a new feature.
1 Like
jmjauer
(Johannes Auer)
5
Could you provide a small example?