• Skip to main content
  • Skip to primary sidebar

Ravi Shankar

Tweaking Apps

  • Swift
  • Tech Tips

TDD

Test Driven Development in Swift

February 9, 2015 By Ravi Shankar Leave a Comment

Here is a beginner tutorial on TDD in Swift by writing a program that checks for a prime number. Let us start by creating a new project, selecting template as Single View Application. Though we won’t be adding anything to the storyboard as we will focus only on the business logic.

201502082101.jpg

201502082103.jpg

After creating the project, the project navigator should have the following structure.

201502082104.jpg

from wikipedia

A prime number is a natural number greater than 1 that no positive divisors other than 1 and itself. – 2,3,5,7,11,13 ….

So now that we know the first prime number is 2, let us add a unit test that checks if number 2 is a prime number. We are going to have a new class called Util that will have the required function to check if a number is prime number. Before creating a Util class, create unit test class called TestsForUtil.swift.

201502082115.jpg

Select the template as Test Case Class and go with the default settings. Now you should see the newly added class as part of the project navigator under CheckForPrimeTests.

201502082117.jpg

As we don’t need CheckForPrimeTests.Swift, we can delete the file. And on opening TestsForSwift.swift, you should notice the following default test methods. setup(), tearDown(), testExampe(), testPerformanceExample(). In this demo, we are not going to use any of these methods and so you can remove them as well.

Let us add our first unit test that checks if number 2 is prime number. Add the following method,

func testTwoIsPrime() {

let number:Int = 2;

XCTAssertTrue(Util().isPrime(number), “2 is a prime number”);

}

You should see use of Unresolved identifier “Util as we are yet to add the class.

In TDD we write the tests first and then add the required functionality. Test Driven Development will ensure that you add only required code to pass your tests.

What this test function does is, it calls isPrime function in Util and receives if a boolean on whether the entered number is prime number. This unit test will show Pass status when the value received from isPrime is true.

Now add a Swift file with name as Util and make sure to select CheckForPrimeTests under Targets. This would ensure you can call functions written in Util class

201502082151.jpg

201502082152.jpg

Create a public class with name as Util and add function isPrime as shown below.

public class Util {

  

func isPrime(number:Int) -> Bool {

return number == 2

}

}

All we are doing here is to make sure the function validates number 2. Now executing unit test should show a green tick mark as shown below.

201502090829.jpg

Navigate back to TestsForUtil.swift and add second tests which checks for number 3.

  func testThreeIsPrime() {

let number:Int = 3;

XCTAssertTrue(Util().isPrime(number), “3 is a prime number”);

}

On executing this test you should notice failure message as we have hard coded isPrime function to work only for 2.

201502090831.jpg

And to make this test pass, we are going to check for 2 and 3 in isPrime function.

  func isPrime(number:Int) -> Bool {

return (number == 2) || (number == 3)

}

Let us add the unit test that checks for 4 which is not a prime number.

  func testFourIsPrime() {

let number:Int = 4;

XCTAssertFalse(Util().isPrime(number), “4 is not a prime number”);

}

We have used XCTAssertFalse as we are expecting isPrime to return false. This test would pass with out making any changes to isPrime function.

201502090838.jpg

Now let us add out next test case that checks for number 11.

  func testElevenIsPrime() {

let number:Int = 11;

XCTAssertTrue(Util().isPrime(number), “11 is a prime number”);

}

201502090841.jpg

We need to make changes to isPrime function so it returns true for number 11. But we cannot just keeping on hardcoding the numbers. So let us change the logic to handle all the prime numbers.

  func isPrime(number:Int) -> Bool {

  

var primeFlag:Bool = true

  

if ((number == 2) || (number == 3)) {

return primeFlag

}

  

if (number > 3) {

  

for index in 2…number-1 {

if (number % index == 0) {

primeFlag = false

break

}

}

}

  

return primeFlag

}

The above function would validate all prime and not a prime numbers and test written for number 11 should pass. Now you can write some tests for prime and not a prime number. For not a prime number make sure to use XCTAssertFalse.

  func testThirtyOneIsPrime() {

let number:Int = 31;

XCTAssertTrue(Util().isPrime(number), “31 is a prime number”);

}

  

func testFiftyIsPrime() {

let number:Int = 50;

XCTAssertFalse(Util().isPrime(number), “50 is not a prime number”);

}

Now let us check this logic for a negative number say -1. Negative numbers are not prime number so isPrime function should handle this. But this test would fail as we don’t have any check for negative numbers.

  func testMinusOneIsPrime() {

let number:Int = –1;

XCTAssertFalse(Util().isPrime(number), “-1 is not a prime number”);

}

Making a minor modifications to isPrime function should pass the test.

func isPrime(number:Int) -> Bool {

  

var primeFlag:Bool = true

  

if ((number == 2) || (number == 3)) {

return primeFlag

}

  

if (number > 3) {

  

for index in 2…number-1 {

if (number % index == 0) {

primeFlag = false

break

}

}

} else {

primeFlag = false

}

  

return primeFlag

}

And the test navigator in Xcode should show status for all your tests.

201502090920.jpg

The logic used in isPrime function can be improved and you can probably do that as your exercise. And make sure all the unit tests have green tick mark after changing isPrime function.

Download the source code from here.

Filed Under: Develop, ios, Programming, Xcode Tagged With: Swift, TDD, Test Driven Development, Xcode

Test Driven Development in iOS – Beginners tutorial – Part 2

April 14, 2014 By Ravi Shankar 3 Comments

This is in continuation of the Test Driven Development in iOS – Beginners tutorial and this covers writing XCTest user interfaces in iOS. AddingTwoNumbers app will have following controls

1. Three textfields – For entering the numbers and displaying the result.

2. Two buttons – Add button to perform addition and Reset button to clear all the textfields.

Let us create a new Test case class for testing the user interface, AddTwoNumberViewControllerTests.m

201404141732.jpg

201404141743.jpg

The first test written is for checking the App Delegate call, storyboard, view controller and view.

-(void)testViewControllerViewExists {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

XCTAssertNotNil([viewController view], @”ViewController should contain a view”);

}

The following needs to be done for passing this test.

1. Add new Objective-C class, subclass of UIViewController (RSViewController).

201404141858.jpg

2. Add a Storyboard file to your project which would contain the ViewController required for this app.

201404142108.jpg

3. Drag and drop, UIViewController on the storyboard and set the class name for the ViewController as RSViewController and StoryboardID as RSViewViewController.

201404142114.jpg

4. Now add the code required in App Delegate’s didFinishLaunchingWithOptions method. This should load the storyboard then instantiate the view controller and set it as RootViewController.

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:[NSBundle mainBundle]];

UIViewController *vc =[storyboard instantiateInitialViewController];

  

// Set root view controller and make windows visible

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

self.window.rootViewController = vc;

[self.window makeKeyAndVisible];

return YES;

}

A lot has been added to just pass a single test case, probably this can be broken up by writing some more tests.

201404142126.jpg

Next add the test for checking the FirstNumber UITextField connection.

-(void)testFirstNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

Next add the test for checking the FirstNumber UITextField connection.

-(void)testFirstNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

This test would pass after adding the IBOutlet for UITextField in the interface file.

#import <UIKit/UIKit.h>

@interface RSViewController : UIViewController

@property (nonatomic,strong) IBOutlet UITextField *firstNumberTextField;

@end

Then add UITextField to the ViewController and make the connection to UITextField with IBOutlet property.

201404142141.jpg

201404142152.jpg

Then add similar test cases for secondNumber and result textfields. Also make corresponding changes to pass the test.

-(void)testSecondNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController secondNumberTextField], @”secondNumberTextField should be connected”);

}

-(void)testresultTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController resultTextField], @”resultTextField should be connected”);

}

201404142205.jpg
Before we move on to the Button test cases, let us refactor the test cases code and remove the duplication of code. The common code has been moved to setup method and now the test cases should loo lot simpler.

@implementation AddTwoNumberViewControllerTests

{

RSViewController *viewController;

}

– (void)setUp

{

[super setUp];

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

}

– (void)tearDown

{

[super tearDown];

}

-(void)testViewControllerViewExists {

XCTAssertNotNil([viewController view], @”ViewController should contain a view”);

}

-(void)testFirstNumberTextFieldConnection {

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

-(void)testSecondNumberTextFieldConnection {

XCTAssertNotNil([viewController secondNumberTextField], @”secondNumberTextField should be connected”);

}

-(void)testresultTextFieldConnection {

XCTAssertNotNil([viewController resultTextField], @”resultTextField should be connected”);

}

Now add the failing test case for Add button and see if the button exists.

-(void)testAddButtonConnection {

XCTAssertNotNil([viewController addButton], @”add button should be connected”);

}

And to pass the above test, add button to ViewController, create a addButton IBOutlet property of type UIButton and make connection to IBOutlet and button field.

@property (nonatomic,strong) IBOutlet UIButton *addButton;


201404142226.jpg

Now add the next test case for checking IBAction method. The below method checks whether addButton call selector method addNumbers on UIControlEventTouchUpInside.

-(void)testAddButtonCheckIBAction {

NSArray *actions = [viewController.addButton actionsForTarget:viewController

forControlEvent:UIControlEventTouchUpInside];

XCTAssertTrue([actions containsObject:@”addNumbers:”], @””);

}

Add this method to RSViewController.m and link this to touchUpInside event of addButton.

-(IBAction)addNumbers:(id)sender {

  

}

Then add the below test cases for checking the addition of two numbers.

-(void)testAddingTenPlusTwentyShouldBeThirty {

viewController.firstNumberTextField.text = @”10″;

viewController.secondNumberTextField.text = @”20″;

[viewController.addButton sendActionsForControlEvents: UIControlEventTouchUpInside];

XCTAssertEqualObjects(viewController.resultTextField.text,@”30″,”Result of the textfield should be 30″);

}

Make changes to the addNumbers method in RSViewController.m by calling application logic method in Addition.m

-(IBAction)addNumbers:(id)sender {

RSAddition *addition = [[RSAddition alloc] init];

NSInteger result = [addition addNumberOne:[self.firstNumberTextField.text integerValue] withNumberTwo:[self.secondNumberTextField.text integerValue]];

self.resultTextField.text = [NSString stringWithFormat:@”%d”,result];

}
Now repeat the test cases for reset button which clears all the textfields.

-(void)testResetButtonConnection {

XCTAssertNotNil([viewController resetButton], @”reset button should be connected”);

}

-(void)testResetButtonCheckIBAction {

NSArray *actions = [viewController.resetButton actionsForTarget:viewController

forControlEvent:UIControlEventTouchUpInside];

XCTAssertTrue([actions containsObject:@”resetFields:”], @””);

}

-(void)testResetButtonShouldClearFields {

viewController.firstNumberTextField.text = @”10″;

viewController.secondNumberTextField.text = @”20″;

viewController.resultTextField.text = @”30″;

[viewController.resetButton sendActionsForControlEvents: UIControlEventTouchUpInside];

XCTAssertEqualObjects(viewController.firstNumberTextField.text, @””, @”FirstNumber textfield should be empty”);

XCTAssertEqualObjects(viewController.secondNumberTextField.text, @””, @”SecondNumber textfield should be empty”);

XCTAssertEqualObjects(viewController.resultTextField.text, @””, @”Result textfield should be empty”);

}

And the corresponding changes to the user interface and RSViewController.m file

-(IBAction)resetFields:(id)sender {

self.firstNumberTextField.text = @””;

self.secondNumberTextField.text = @””;

self.resultTextField.text = @””;

}

Though lot more test cases can be added, I believe this is a decent starter project for learning TDD in iOS. And it is always nice to see the green mark in test navigator.
201404142302.jpg
Now you should be able to run your app and test the functionality.

201404142303.jpg
Source code can be downloaded from here

Filed Under: ios, iPhone, Xcode Tagged With: TDD, User Interface, Xcode, XCTest

Test Driven Development in iOS – Beginners tutorial – Part 1

April 13, 2014 By Ravi Shankar 7 Comments

This is a beginner tutorial on TDD in iOS, explained with an example app that adds two numbers. Let us see try to use some XCTest assertions supported in iOS in this project.

Create a New iOS project select the template as Empty Application

201404132059.jpg

Enter the required details in Choose options for your new project screen.

201404132104.jpg

Step 3: Then specify the folder to save this project.

201404132106.jpg

The project navigator screen will have two targets, one for app and add another for XCTest framework. The folder that ends with “Tests” will contain the default XCTest file .

Lets use the class AddingTwoNumbersTests.m for testing the application logic. Open AddingTwoNumbersTests.m and delete default testExample method.

201404132127.jpg

Create a failing method that tests for existence of new “RSAddition” class that is going to have method for adding two numbers.

201404132155.jpg  

You should notice the errors displayed in the above screenshot after adding testAdditionClassExists. To fix these error, create a new class named RSAddition subclass of NSObject and add the class to both the targets. Then import the Addition.h in “AddingTwoNumbersTests.m”.

201404132152.jpg

201404132153.jpg

Now the tests should pass when it gets executed. You should notice the green tick mark before the class and method and shown in the below screenshot.

201404132158.jpg

Now add the following method to do a simple addition of 2+2.

-(void)testAddTwoPlusTwo {

RSAddition *addition = [[RSAddition alloc] init];

NSInteger result = [addition addNumberOne:2 withNumberTwo:2];

XCTAssertEqual(result, 4, @”Addition of 2 + 2 is 4″);

}

RSAddition class needs to have a new method addNumberOne: withNumberTwo: Add the method definition in RSAddition.h interface file.

-(NSInteger)addNumberOne:(NSInteger) firstNumber withNumberTwo:(NSInteger) secondNumber;

Then add the following method to RSAddition.m implementation file. Let us hardcode the result “4” for time being to pass this test.

-(NSInteger)addNumberOne:(NSInteger) firstNumber withNumberTwo:(NSInteger) secondNumber {

return 4;

}

Now the test should get executed successfully.
201404132209.jpg
Let us add another test to fix the hardcoding problem. This time we will add two different numbers 2 and 7. But before adding the test method, we need to refactor the existing code in the test file. The below code is used by both the test methods and this is the best candidate to be placed under setup method.

  RSAddition *addition = [[RSAddition alloc] init];

The changed code should look as shown in the below screenshot.

201404132223.jpg

Adding the below method should fail as the addition method is hardcoded to return value as 4.

-(void)testAddTwoPlusSeven {

NSInteger result = [addition addNumberOne:2 withNumberTwo:7];

XCTAssertEqual(result, 9, @”Addition of 2 + 7 is 9″);

}

201404132226.jpg

Now edit the addition method to fix this problem. It requires just a one line change and now the test should pass.

201404132229.jpg

201404132231.jpg

The next tutorial will cover the steps for adding the view controller and required fields for adding two numbers using the above added application logic class.

Download source code from here.

Filed Under: ios, iPhone, Xcode Tagged With: Assertions, iOS, TDD, Xcode, XCTest

Primary Sidebar

TwitterLinkedin

Recent Posts

  • How to block keywords in Jio broadband
  • How to disable opening an app automatically at login in Mac
  • How to set preferred Wifi network on Mac
  • Attribute Unavailable: Estimated section warning before iOS 11.0
  • How to recover Firefox password from Time Machine backup

Pages

  • About
  • Privacy Policy
  • Terms and Conditions

Copyright 2022 © rshankar.com

Terms and Conditions - Privacy Policy