Performance testing in Swift using TDD - ios

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).

Related

Why are swift functions so expensive?

Why are swift functions so expensive?
I have been writing an app using SpriteKit. Every frame, I recalculate the position of the CameraNode in the update() function. Lets call this the //(cameraNodeCode). This current setup had little influence on the frames per second, it stayed at 60.
override func update() {
//(cameraNodeCode)
}
As the cameraNodeCode is quite large, I thought it would be better to simplify my code and put it into a function: updateCameraNode(). Now this was what I had:
func updateCameraNode() {
//(cameraNodeCode)
}
override func update() {
updateCameraNode()
}
When I set up the code like this, the frames per second suddenly dropped to 20. I was very surprised as I didn't think functions were this expensive to call. I decided to test my theory with this code:
func emptyFunction() {
}
override func update() {
emptyFunction()
emptyFunction()
emptyFunction()
emptyFunction()
emptyFunction()
}
As I predicted, when I did this the frames per second dropped drastically, to 4.1 frames per second!
My questions are:
Why is this happening? Is it as I think and because simply calling a function is so expensive, or is there something I am missing?
Is there a way that I could still keep my code looking simple without having 20 frames per second?
Update
The key information that I left out was that I was using Xcode playgrounds. I think that this is a bug with SpriteKit and playgrounds. I have filed a bug report with Apple so I’ll see where that gets me.
Speaking about Sprite-kit, I've tested your code to my iPhone 7 in a fresh "Hello world" template :
import SpriteKit
class GameScene: SKScene {
private var label : SKLabelNode?
override func didMove(to view: SKView) {
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.alpha = 0.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
}
func emptyFunction() {}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
//emptyFunction()
//emptyFunction()
//emptyFunction()
//emptyFunction()
//emptyFunction()
}
}
If I don't commented the lines (remove // ) inside the update method, nothing change. I've always 60fps. Check your project to find what are the lines that caused this drastic drop of fps, or if you test your code to a simulator try to a real device. Hope it helps.
Swift has three different methods of dispatch with different performance characteristics:
Direct Dispatch should be very fast. Also known as Static Dispatch.
Table Dispatch is a bit slower due to a method lookup in a witness table. Also known as Dynamic Dispatch.
Method Dispatch is the most dynamic dispatch method. However, it is also the slowest one of the three.
You can force the compiler to use static dispatch by adding final to your method:
final func emptyFunction() {
}
This will also give the compiler additional opportunities for optimisation, such as inlining code. Remember to build with optimisations turned on, which is not the case for debug builds. Therefore you should make sure to choose the release configuration for performance testing. Debug builds of Swift projects are often notoriously slow.
See this post on the Swift blog for more information on method dispatch and the static keyword.
This great post explains the three kinds of method dispatch in Swift and when they are used.

Run iterative tests in XCTestCase on the same test

I am attempting to run a UI Test in XCTestCase and when I am running it, I would like to run it multiple times with multiple different inputs. Is there a straightforward way to run a test multiple times with different inputs?
I want to run a test with different inputs but only write the test once. An example of this would be that I am trying to pass different user names in to verify behavior.
It's kind of sloppy because it executes setUp() once at the beginning unnecessarily but otherwise it gets the job done...
func testLoop() {
for parameter in myParameters {
setUp()
testToBeLooped(parameter: parameter)
tearDown()
}
}
Instead of directly running your assertions for each input in the same test I will advise to use XCTContext.runActivity to specify activity name for each parameter input:
func testToBeLooped(parameter: Int) {
print(parameter)
XCTAssertEqual(0, parameter % 2)
}
func testLoop() {
let myParameters = [1, 2, 3]
for parameter in myParameters {
XCTContext.runActivity(named: "Testing for parameter \(parameter)") { activity in
setUp()
testToBeLooped(parameter: parameter)
tearDown()
}
}
}
This allows you to visualize exactly which input parameters didn't pass assertions and which of the assertions failed for each in the test report:

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

iOS UI Unit Testing (XCode7)

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.

Resources