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:
The TEST_HOST flag is set. Your application classes are visible to your tests.
Integration tests instantiate their own assembly, rather than use the application assembly. While this is normally recommended, its important to be aware that this is a separate, additional assembly to the one used in the main app.
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
Note that you may need to call factory.unload() to clear any instances of TyphoonScopeSingleton before-hand.
Also note that TyphoonPatcher also allows unpatching or winding back if you wish.
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
Related
I'm working on a small 2-player card game app for iOS. For now, I've separated this app into 3 modules.
UI
Game logic
Networking
I have two pubic protocols in the networking module:
public protocol ConnectionManager {
init(name: String)
var peers: Property<[String]> { get }
func invitePeer(at index: Int)
func respond(accept: Bool)
var invitation: Signal<String, NoError> { get }
var response: Signal<Bool, NoError> { get }
}
public protocol MessageTransmission {
func send(_ data: Data)
var data: Signal<Data, NoError> { get }
}
and there is actually only one concrete implementation which conforms to both two protocols. Lets' say:
class Foo: ConnectionManager, MessageTransmission {
// ... codes omitted
}
The UI module receives a name from the user and uses it to initialize a Foo. Then it displays the nearby players according to the peers property. When a user commits an invitation to start a new game, UI module forwards this request to the ConnectionManager and the ConnectionManager handles those dirty works.
For the game logic module, it only cares about message transmission, but the transmission depends on the previous "invite-respond" step because we need a target to exchange message with. (the target related concepts are encapsulated, so the game logic only knows there is a thing that it can send message to and receive message from.)
My thought is: once a session is established, i.e., once an invitation is responded with true, the UI module initializes a game logic thing (maybe an instance of a type Game) and passes the ConnectionManager (although UI module initializes an instance of type Foo, it stores that instance as of type ConnectionManager) to it. But the problem is, the ConnectionManager has nothing to do with MessageTransmission when looked from the outside even if they're implemented by a single type internally.
I can do a force case at either side, but it looks tricky.
I can combine those two protocols, but then the UI module and the game logic module interacts with a surplus interface (interface that has more features than needed).
Which path should I take? or is there any better way to do this?
ps: The Foo can only be initialized by UI module because it's the only one who knows about the name. And it's also necessary to pass the same instance to the game logic module because there are some internal states (the target related thing) which is modified in the previous interaction with UI module and effects the later interaction with game logic module. I can't think of another way to do this.
As I understood from your explanation you need to pass MessageTransmition implementation to game logic but do not want to pass ConnectionManager.
As soon as instance of MessageTransmission depends on result of invitation (or accepting invite) made by ConnectionManager you need to build object conformed to MessageTransmition from within ConnectionManager and pass to game logic. Yes, that means that you couple these two entities but they will have clean responsibilities in this case.
Consider this kind of protocol:
protocol ConnectionManager {
func respond(accept: Bool) -> Signal<MessageTransmition, Error>
}
respond() method accepts invitation and builds MessageTransmission conforming object on success or returns error if accepting failed or if invitation declined. This object you can pass to game logic.
I just started playing with frameworks in xcode trying to create my own module. When making an iOS app its relatively straightforward where the entry point is ( AppDelegate )
However the framework I've created has nothing like a "main.swift" or similar method.
Do i have to create/specify the entry point on my own?
Thanks
EDIT: The reason i'd like the framework to be runnable is so that i can actually print some output while I'm making it to make sure everything works properly.
(My TL;DR is at the bottom.)
As already stated, there is no entry point like you are thinking. Instead, you should do this:
In your Framework target (I'll assume the framework is named MyFramework):
Add files, classes, properties, subclassed controls, etc. and mark things as public, private, internal, and fileprivate. (See the access level section in the Apple documentation.)
For instance:
public class MyClass1 {
public var property1 = ""
private var property2 = ""
public func myFunc() -> String {
print("Hello World!")
}
}
private class MyClass2 {
var property1 = ""
var property2 = ""
func myFunc() -> String {
return "Hello World!"
}
}
In your app target (again, assuming your framework is named myFramework):
include MyFramework
class ViewController: UIViewController {
func tryThis() {
let myClass1 = myClass1()
print(myClass1.myFunc()) // prints "Hello World!"
// the line below will generate a build error
// as myClass2 is marks private
let myClass2 = myClass2()
}
}
TL;DR
Learn your Access Levels, add code into your Framework target, and import the framework into your app.
A framework doesn't have a traditional entry point like this - it won't ever be run by itself, so it doesn't need one.
To use your framework you would create an app which linked with your framework - the entry point for the app would then call methods from inside your framework.
An answer I got from #deanWombourne points out that if anyone wants to only use the framework project for development as is and develop the framework without integrating it in an app for execution, they can just use the tests provided by the framework for an entry point.
For someone that might be new, all you need to is include unit tests to your project, press on the play button which you normally press on for running and select the wrench icon that writes "test" next to it to run the tests.
I'm a seasoned Objective-c programmer but I can't say the same for Swift, I'm having a hard time unit testing a class in swift without using frameworks like OCMock.
The Problem: I'm integrating Firebase into a mixed Objective-C/Swift project, and I need to configure it based on the build configuration of the app.
I've written a Swift class for that (that will be used by the obj-c app delegate), however since the firebase framework is configured trough a static class method, precisely FIRApp.configure(with: FIROptions), I need to mock this method somehow in order to unit test it.
My code, without any handle for Dependency Injection, looks like that:
#objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
I've done some research but so far I didn't find nothing that fits my solution, however I was thinking of one of the following:
I could pass a function that defaults to FIRApp.configure(with:) however I should do this from objective-c and the function also accepts a parameter, I was struggling with the syntax
I could use a wrapper around FIRApp, but I wanted to avoid it unless the only viable clean solution.
I could keep on playing with protocols and do dependency inversion, however being the method static I was struggling with the syntax again, I can't find an easy way to do DI with a mock class with a static method.
As a reference (both personal and for who might need it) these are some of the resources I found useful and upon which I will keep on digging:
Dealing with static cling in Swift
This Question
This article about generic unit testing
In the meanwhile, every help would be really appreciated.
As a sidenote, there are many ways I can solve this problem without struggling with mocking a static class method, but my aim here is to find out a way of mocking it in order to have a better understanding of the best practices when testing more complex situations.
You can indeed do any of those.
Closure Argument
You can have your configureFirebase function take an "applier" closure that defaults to what you originally used:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
Protocols
You need a Configurable protocol, and then to conform FIRApp to it for the default case:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
If you're just going to use this in one method, though, it's merely transient state, and it should probably be a function argument rather than stored property.
(If it's unclear whether it's persistent or transient state because the whole point of the class is to call a single function, perhaps you don't even need a class, just a function.)
I am developing an iOS application and am trying to integrate Typhoon into the testing. I am currently trying to mock out a dependency in a view controller that comes from the storyboard, so with in my assembly:
public dynamic var systemComponents: SystemComponents!
public dynamic func storyboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
(definition) in
definition.useInitializer("storyboardWithName:factory:bundle:") {
(initializer) in
initializer.injectParameterWith("Main")
initializer.injectParameterWith(self)
initializer.injectParameterWith(NSBundle.mainBundle())
}
}
}
I want to create a CameraModeViewController (the class I am unit testing) with its dependency upon a system-camera-functions-providing protocol mocked out. The dependency is dynamic var cameraProvider: CameraAPIProvider?. I think I correctly created a replacement collaborating assembly to replace systemComponents; MockSystemComponents is a subclass of SystemComponents that overrides functions. This is where I inject the mock:
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
MockSystemComponents(camera: true)
])
let storyboard = assembly.storyboard()
subject = storyboard.instantiateViewControllerWithIdentifier("Camera-Mode") as! CameraModeViewController
The next line of code in the tests is let _ = subject.view, which I learned is a trick to call viewDidLoad and get all the storyboard-linked IBOutlets, one of which is required for this test.
However, I am getting very mysterious result: sometimes but not always, all the tests fail because in the viewDidLoad I make a call to the dependency (cameraProvider), and I get an "unrecognized message sent to class" error. The error seems to indicate that at the time the message is sent (which is a correct instance method in protocol CameraAPIProvider) the field is currently a CLASS and not an instance: it interprets the message as +[MockSystemCamera cameraStreamLayer] as reported in the error message.
~~~BUT~~~
Here's the kicker: if I add a breakpoint between the calls to assembly.storyboard() and subject.view, the tests always pass. Everything is set up correctly, and the message is correctly sent to an instance without this "class method" bogus interpretation. Therefore, I have to wonder if Typhoon does some kind of asynchronous procedure in the injection that I have to wait for? Possibly only when dealing with storyboard-delivered view controllers? And if so, is there any way to make sure it blocks?
After digging around in Typhoon's source for a while, I get the impression that in the TyphoonDefinition(Instance Builder) initializeInstanceWithArgs:factory: method there is an __block id instance that is temporarily a Class type, and then is replaced with an instance of that type; and possibly this can be called asynchronously without blocking, so the injected member is left as a Class type?
UPDATE: Adding the code for MockSystemComponents(camera:). Note that SystemComponents inherits from TyphoonAssembly.
#objc
public class MockSystemComponents: SystemComponents {
var cameraAvailable: NSNumber
init(camera: NSNumber) {
self.cameraAvailable = camera
super.init()
}
public override func systemCameraProvider() -> AnyObject {
return TyphoonDefinition.withClass(MockSystemCamera.self) {
(definition) in
definition.useInitializer("initWithAvailable:") {
(initializer) in
initializer.injectParameterWith(self.cameraAvailable)
}
}
}
}
UPDATE #2: I tried replacing the constructor injection in the MockSystemComponents.systemCameraProvider() with a property injection. Different issue, but I suspect it's equivalent in cause: now, the property that is injected (declared optional) is still nil some of the time when I go to unwrap it (but not always -- probably about 4/5 of test runs fail, about the same as before).
UPDATE #3: have tried using the following code block, using factory construction according to this answer (note that setting factory directly didn't work as that OP did, but I think I correctly used the feature added in response to Jasper's issue). The results are the same as when using property injection like Update #2 above), so no dice there.
This issue was in fact arising even before the call to the instantiation. In fact, the problem was assemblies aren't generally intended to be stateful. There are a few ways to get around this, but the one I used -- having a member variable and an initializer method -- is NOT recommended. The problem with doing this is that in the activateWithCollaboratingAssemblies method, all the instance methods of the assembly are enumerated for definitions, and initializers will actually get called on the collaborating assembly. Consequently, even if you create your assembly with an initializer, it may get called again with a bogus value.
Note that the reason there appeared to be async behavior is actually that there is nondeterministic order in which definitions are assembled (property of storing them in an NSDictionary). This means that if activateWithCollaboratingAssemblies happens to enumerate methods which depend on state first, they'll work fine; but if the initializer is enumerated first, and the state is destroyed, definitions that are created after will be borked.
I'm trying to port an objc app which uses Facebook's KVOController, to Swift. I've been encouraged to look at RC3 as an alternate and more Swiftish approach. I've read some blogs and I'm encouraged to give this a try. But much of the docs and blogs seem to concentrate on sockets and timers as examples. So I have two simple questions right now:
1) Given an objc fragment like:
[self.KVOController observe: self.site keyPath: #"programs" options: NSKeyValueObservingOptionInitial block:^(id observer, id object, NSDictionary *change) {
[self.tableView reloadData];
}];
What is the simple way to rewrite this with RC3 APIs? And what is the RC3 equivalent for self.KVOController unobserve: self.site that happens when that view unloads?
2) It's recommended that I use Carthage to grab the RC3 code. Can I intermix a Cartfile safely with a project that is using cocoa pods already for Alamofire and SwiftyJSON?
Disclaimer :)
I'm also new to the whole ReactiveCocoa concept and especially to RAC 3. I'm playing around with the new framework to learn, so my solution may not be the intended usage of the API. I'd be happy to receive feedback from others and learn the right way.
Here's what I could come up with for your first question:
You can define the property you'd want to observe as a MutableProperty. Below is the most basic example with a String property. Assume you have a view model, it has a String property and you want to observe this in your view controller object.
ViewModel.swift
class ViewModel {
var testProp = MutableProperty<String>("")
}
ViewController.swift
class ViewController: UIViewController {
private let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let testPropObserver = viewModel.testProp.producer
testPropObserver.start(next: {value in
println("new value \(value)")
})
viewModel.testProp.value = "you should see this in your log"
}
}
For the first question I can only guide you in the right direction. Take a look at the change log to 3.0 describing the shift away from RACObserve() to PropertyTypes. It's a little more explicit and it offers type safety. Here is the source, which I think is your best bet for figuring out how to implement it. You could also take a look at their test cases to see if they have built any tests for the PropertyType protocol and mimic how they've got it set up.
And yes, you'll have no trouble mixing Carthage and CocoaPods in one project, as Carthage is designed to be minimally intrusive. As long as you follow the instructions on their website you should be fine.