iOS UI Unit Testing (XCode7) - ios

I'm a bit confused with the new UI Unit Testing scheme that apple released in their XCode7 Beta. I think it's an awesome idea, but I have a couple questions.
this is one testing method I have...
func testMetricsProperties() {
// Used some of the metrics for testing for reference
let app = XCUIApplication()
app.scrollViews.descendantsMatchingType(.Unknown).containingType(.StaticText, identifier:"rim").childrenMatchingType(.Button).element.tap()
app.textFields["_XCUI:Secure"].typeText("")
app.typeText("\r")
app.buttons["dash metrics"].tap()
let element = app.descendantsMatchingType(.Unknown).containingType(.Image, identifier:"darkBackground.png").childrenMatchingType(.Unknown).element.childrenMatchingType(.Unknown).elementBoundByIndex(1).childrenMatchingType(.Unknown).element.childrenMatchingType(.Unknown).element
let offPlanRevenue = element.childrenMatchingType(.Unknown).elementBoundByIndex(0).staticTexts["OFF PLAN REVENUE"]
offPlanRevenue.tap()
XCTAssert(offPlanRevenue.exists);
XCTAssertEqual(offPlanRevenue.value as! String, "");
}
However, in the next testing method, it seems that I have to load the entire app again,
let app = XCUIApplication()
app.scrollViews.descendantsMatchingType(.Unknown).containingType(.StaticText, identifier:"im").childrenMatchingType(.Button).element.tap()
app.textFields["_XCUI:Secure"].typeText("")
app.typeText("\r")
app.buttons["dash metrics"].tap()
}
Is there anyway I can avoid this? This can be troublesome if i'm trying to run a full test on an entire suite.

I believe what you are looking for is using the setUp() and tearDown() methods. setUp() gets called before each test method and tearDown() gets called after each test method for a class.
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
Use these to clean up between testing methods back to the app's original state.

Related

Swift initializer call Dispatch Queue async

Is it wrong to call async from Swift object initializer such as this one
let serialQueue = DispatchQueue(label: "com.myApp.SerialQueue")
private let property1:Int?
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
private func setupProperties() {
self.property1 = 1
}
private func nonBlockingSetup() {
//Some non-blocking code that shouldn't run on main thread
}
Some people say async call is problematic before init returns. Need to know what Swift language says about it.
EDIT: Is there any difference if I modify the code as follows:
public override init()
{
super.init()
/* Initialize properties */
setupProperties()
callNonBlockingCodeAsync()
}
private func callNonBlockingCodeAsync() {
serialQueue.async { [unowned self] in
self.nonBlockingSetup()
}
}
To answer your question, I tried out the simple example.
Errors are very much self explanatory, in the initialisation process dispatchQueue are capturing self reference right before it's actual initialisation.
You are running into the concurrency problem where initialisation of object is necessary before using it.
dispatchQueue uses closures to provide DispatchWorkItem and as you know closures captures values surrounding it's scope.
Update
One work around would be to give default values to your properties but
I am not sure if that will help you.
In general, a constructor should not do any meaningful work.
Having a constructor that executes code delayed (because it's async) will be unexpected for anyone using that class (quite possibly including you in 6 months), and can therefore lead to bugs. In such cases it's usually better to have a separate initialization method, which makes it clear to an api user that there is something more going on.
If you absolutely want to make sure the initialization method is called, I usually make the constructor private and add a class method for construction. Again this signals api users that there is something going on behind the scenes.

NSObject's initialize() not called in Release build configuration

According to Apple documentation initialize() method Initializes the class before it receives its first message.
Can somebody explain why initialize() is not working in Release build configuration?
For example:
class Test: NSObject {
override class func initialize() {
print("initialize")
}
class func test() {
print("test")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Test.test()
}
}
Output in Debug configuration:
initialize
test
Output in Release configuration:
test
I did a quick test and it looks like in Release configuration + initialize is not called unless you create an instance of the class. However in Debug calling a class method is enough to trigger +initialize. Looks like an undocumented caveat.
Edit:
Even more interesting fact is that for Objective-C project in both Debug and Release configurations calling a class method is enough to trigger + initialize. I would say this is a bug. You might want to file a radar for it.

How To Unit Test an #IBAction with asyncronous call

In the following code, I want to test whether "DisplayHelper.displayAlert" has been called. I am dependency-injecting DisplayHelper and AuthService mock objects, and using PromiseKit
In My ViewController:
#IBAction func submitButtonPressed(sender: AnyObject) {
AuthService.updateUser(["zipcode": self.zipcodeTextField.text!])
.then { user -> Void in
// sucess code
}.error { error -> Void in
self.DisplayHelper.displayAlert("Zipcode Not Found", message: "We can't find that zipcode... please try again.", callingViewController: self)
}
}
Here are the not-working tests:
func testSubmitButtonServerCantFindZipcode() {
vc.zipcodeTextField.text = "00000"
vc.submitButtonPressed(self)
// called AuthService (passes)
XCTAssertEqual(authServiceMock.updateUserCalled, true)
// called displayAlert (fails because tests run before DisplayAlert is called)
XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}
How do I get the tests to wait for all of the code to execute before the assertions?
When testing asynchronous code with XCTest you need to use XCTestExpectation.
You can rewrite the test code like this:
let e = expectationWithDescription("display alert")
waitForExpectationsWithTimeout(3) { error in
XCTAssertEqual(displayHelperMock.displayAlertCalled, true)
}
Now the only missing piece to make the test work is finding a place where to call expectation.fulfill(). The most appropriate place would be in your AuthService mock, after the success and failure callbacks are run.
If I can suggest you something though, writing tests that asser whether certain methods have been called is not a safe approach to testing, as you are just testing implementation rather than behaviour.
What could be a better approach is to test the two componets independently. TestAuthService making sure that both the success and failure path execute as expected, and the DisplayHelper to make sure that the alert view is actually added to the view hierarchy.
This article could be a useful place to start to find out how unit test alerts, and this post is a good read on why and how to avoid mocks.

Unit test is incorrectly executing code from the launch view controller

I have a simple struct as below which is part of an iOS Application:
struct Country: JSONObject {
let name: String!
let code: String!
static let nameKey = "name"
static let codeKey = "dial_code"
init?(_ json: [String: AnyObject]) throws {
guard let name = json[Country.nameKey] as? String else {
throw JSONError.InvalidTypeForKey(key: Country.nameKey)
}
guard let code = json[Country.codeKey] as? String else {
throw JSONError.InvalidTypeForKey(key: Country.codeKey)
}
self.name = name
self.code = code
}
}
I wrote a unit test to test initialisation of this class:
func testCorrectInitialisationOfCountry() {
let countryDict = [Country.nameKey: "England", Country.codeKey: "+44"]
do {
let country = try Country(countryDict)!
XCTAssert(countryDict[Country.nameKey] == country.name, "The country name does not match")
XCTAssert(countryDict[Country.codeKey] == country.code, "The country code does not match")
}
catch _ {
print("Initialisation of country failed with an exception")
}
}
The problem I am facing is this struct is initialised several times in the viewDidLoad() method in the first view controller of my app.
For some reason, that viewDidLoad() method is being called when I run my tests and generating incorrect code coverage because of that. The picture below shows the unit test stats generated by Xcode.
The numbers "241" should actually be just "1". The other 240 times, that line is being executed from the launch view controller which is not under test.
How can I stop the view controller code from executing?
Thanks in advance.
Unit tests are run inside of the context of your running app.
I think your best bet is to write your app delegate's didFinishLaunchingWithOptions to not bring up the view controller if started by a test (or bring up a simpler one)
This answer offers one way to check if your app is running in a unit test:
https://stackoverflow.com/a/30306450/3937
To stop the automatic loading of the first storyboard, remove it as the launch storyboard and code the loading yourself following this answer:
https://stackoverflow.com/a/16702730/3937
You should use the setUp method (inside your unit test class) which is invoked AFTER the viewDidLoad.
Override this method to customize the initial state for all tests in the test case.
This way, you can reset whatever you need just before your test methods execute.
For example, I needed to reset a singleton that was invoked via a view controller. Note that you can reset between each method via the tearDown method:
called once after each test completes. Override this method to perform any per-test cleanup.
There is a solution for your problem. You can short circuit your app launch as described in link below.
How To: Unit Test Without Running The Entire App

Performance testing in Swift using TDD

Just learning Test Driven Development in Swift.I created a class that is subclass of XCTestCase in the "ProjectNameTests" group.
class BasicFunctionTest: XCTestCase {
var values : [Int]?
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
for i in 1...100000{
println("ok i need to know the time for execution")
}
}
}
}
I want to perform performance testing of these method
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock() {
// Put the code you want to measure the time of here.
for i in 1...100000{
println("ok i need to know the time for execution")
}
}
}
and want to find out the time for execution of the for loop.I run the project on going to Product -> Perform Action - > Test Without Building.
I can see the execution time as 3.50 sec but when i try to set the baseline clicking on the Stroke Area as below image:
I get warning as as
Would you like to update the baseline values for all tests on all
devices? You'll have a chance to review the changes the next time you
commit to source control.
Now when i click on Update the error is as below:
How can i set Baseline of the time for my specific method as Here
Here is my test project :
https://drive.google.com/file/d/0Bzk4QVQhnqKmUzYyclp6ZnJvekE/view?usp=sharing
Can you check whether BasicFunctionTest file Target membership includes your test target. You should be able to check this by selecting the file and navigating to File Inspector (target membership).

Resources