Is it possible to swap or patch an assembly when running a UI test on iOS? Currently I have a project where I have setup Typhoon framework and I am able to patch an assembly like this
var controller: HomeViewController!
override func setUp() {
super.setUp()
// setup assemblies
var blAssembly = BusinessLogicAssembly()
var ctrlAssembly = AppAssembly()
// setup patcher
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("testManager", withObject: {return FakeManager()})
patcher.patchDefinition(blAssembly.testManager() as TyphoonDefinition, withObject: {
return FakeManager()})
let factory = TyphoonBlockComponentFactory(assemblies: [blAssembly, ctrlAssembly])
factory.attachPostProcessor(patcher)
// get controller
controller = factory.componentForKey("homeViewController") as HomeViewController
// force view to laod
let vcView = controller.view
}
And this is working fine. It patches the TestManager
with a stub. But in this case I am manually invoking my view controller. When I run a UI test (where the controllers are handled behind the scenes when the app is launched) is there a way of patching an assembly and providing a mock/stub?
For instance lets say I have a view controller which is calling a web service. The web service logic is wrapped in a separate class and is injected into the view controller with a TyphoonAssembly
. Now for my UI tests I do not want to contact my actual web services but just proved sample data. I am imagining doing this by creating a stub of my web service class and returning test data. Can this be achieved with the Typhoon framework because I was unable to do it or find an example anywhere.
I am using Swift but Objective-C answers will work too (as long as it is compatible)
Now there's a few ways with Typhoon that one component can be swapped for another. We could use TyphoonPatcher or modularize assemblies. Or we could even make a custom TyphoonDefinitionPostProcessor or TyphoonInstancePostProcessor.
The problem/solution:
But if I'm understanding your question correctly, the problem is that application-style (default) kind of tests are being used, where:
If you want to modify the main app's assembly from tests, we can use defaultFactory
. If you're using plist integration:
Create a definition for you app delegate:
dynamic func appDelegate() -> AnyObject {
return TyphoonDefinition.withClass(AppDelegate.self) {
(definition) in
//This is a public instance var of type TyphoonComponentFactory
definition.injectProperty("assembly", with: self)
}
}
. . and then in your AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
factory.makeDefault()
}
. . . now in your tests or wherever you need, you patch the app's assembly, by getting a hook to it:
TyphoonComponentFactory *factory = [TyphoonComponentFactory defaultFactory];
[factory unload]; //clear out any singletons
//Now patch it
factory.unload()
to clear any instances of TyphoonScopeSingleton
before-hand.Alternative:
Alternatively, and simpler (we recommend simple wherever possible), perhaps you could just start up the app with a different network assembly specifying a different set of assemblies in the application plist.
*NB: In Objective-C any TyphoonComponentFactory
can be cast to an Assembly and vice-versa. This is not allowed in Swift, so might create some inconvenient limitations. To be addressed by #253