unit tesing in ios

84
Unit Testing

Upload: ciklum

Post on 06-May-2015

563 views

Category:

Software


3 download

DESCRIPTION

iOS Practice Leaders Community Meet-up. “Unit Testing in iOS” by Maxim Koshtenko - why we need tests and what their use in applications’ developing on a project is; - how one should and should not test source code; - review of some of the most popular tools which make test-writing easier; - how to switch to unit-testing on a project which already exists.

TRANSCRIPT

Page 1: Unit Tesing in iOS

Unit Testing

Page 2: Unit Tesing in iOS

Unit Testing

What Is Testing For? 

Page 3: Unit Tesing in iOS

Unit Testing

When Should Software Be Tested?

What Is Testing For? 

Page 4: Unit Tesing in iOS

What Is Testing For?

Page 5: Unit Tesing in iOS

What Is Testing For?

Testing can show that the product works!

Page 6: Unit Tesing in iOS

When Should Software Be Tested?

Page 7: Unit Tesing in iOS

Waterfall project management process

Page 8: Unit Tesing in iOS

Waterfall project management process

Requirements

Page 9: Unit Tesing in iOS

Waterfall project management process

Requirements

Specification

Page 10: Unit Tesing in iOS

Waterfall project management process

Requirements

Development

Specification

Page 11: Unit Tesing in iOS

Waterfall project management process

Requirements

Test

Development

Specification

Page 12: Unit Tesing in iOS

Waterfall project management process

Requirements

Test

Development

Specification

Deployment

Page 13: Unit Tesing in iOS

Cost of Bugs Time Detected

Time Introduced Requirements Architecture Coding System Test Post-

Release

Requirements 1 3 5-10 10 10-100

Architecture - 1 10 15 25-100

Coding - - 1 10 10-25

Cost of Fixing Bugs Found at Different Stages of the Software Development Process

Page 14: Unit Tesing in iOS

When Should Software Be Tested?

Page 15: Unit Tesing in iOS

When Should Software Be Tested?

Software should be tested all the time!

Page 16: Unit Tesing in iOS

Unit Testing

Page 17: Unit Tesing in iOS

What is Unit Test?

Unit tests are small pieces of code that test the behavior of other code.

Page 18: Unit Tesing in iOS

A test is not a unit test if:

• It talks to the database

• It communicates across the network

• It touches the file system

• It can't run at the same time as any of your other unit tests

• You have to do special things to your environment (such as editing config files) to run it.

Page 19: Unit Tesing in iOS

Properties of a Good Unit Test

Page 20: Unit Tesing in iOS

A good unit test:

is able to be fully automated

Page 21: Unit Tesing in iOS

A good unit test:

Tests a single logical concept in the system

Page 22: Unit Tesing in iOS

A good unit test:

Consistently returns the same result (no random numbers, save those for integration

tests)

Page 23: Unit Tesing in iOS

A good unit test:

is Maintainable and order-independent

Page 24: Unit Tesing in iOS

A good unit test:

is Independent

Page 25: Unit Tesing in iOS

A good unit test:

Runs fast

Page 26: Unit Tesing in iOS

A good unit test:

is Readable

Page 27: Unit Tesing in iOS

A good unit test:

is Trustworthy (when you see its result, you don’t need to debug the code just to be sure)

Page 28: Unit Tesing in iOS

A good unit test is:• Able to be fully automated

• Tests a single logical concept in the system

• Consistently returns the same result

• Maintainable and order-independent

• Independent

• Runs fast

• Readable

• Trustworthy

Page 29: Unit Tesing in iOS

Verifications

Page 30: Unit Tesing in iOS

Types of Verifications

•Return Value

•State

•Behavior

Page 31: Unit Tesing in iOS

Types of Verifications

SUT

Page 32: Unit Tesing in iOS

Types of Verifications

SUTBehavior (Indirect Outputs)

DOC

Page 33: Unit Tesing in iOS

Types of Verifications

SUTSetup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

DOC

Page 34: Unit Tesing in iOS

Return Value Verification

We inspect the value returned from the system under test and compare it to the

expected state.

SUTSetup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

DOC

Page 35: Unit Tesing in iOS

State Verification

We inspect the state of the system under test after it has been exercised and compare

it to the expected state.

SUTSetup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

DOC

State

Page 36: Unit Tesing in iOS

SUT

Behavior Verification

We capture the indirect outputs of the SUT as they occur and compare them to the

expected behavior.

Setup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

DOC

Page 37: Unit Tesing in iOS

SUT

Behavior Verification

We capture the indirect outputs of the SUT as they occur and compare them to the

expected behavior.

Setup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

Fake

Verify

Page 38: Unit Tesing in iOS

XCTest

Page 39: Unit Tesing in iOS

XCTest• Provided by Xcode

Page 40: Unit Tesing in iOS

XCTest• Provided by Xcode

• New iOS application projects automatically include a unit testing target

Page 41: Unit Tesing in iOS

XCTest• Provided by Xcode

• New iOS application projects automatically include a unit testing target

• Test classes do not have a header file

Page 42: Unit Tesing in iOS

XCTest• Provided by Xcode

• New iOS application projects automatically include a unit testing target

• Test classes do not have a header file

• It’s not necessary to add application’s source files to the XCTest Target

Page 43: Unit Tesing in iOS

XCTest• Provided by Xcode

• New iOS application projects automatically include a unit testing target

• Test classes do not have a header file

• It’s not necessary to add application’s source files to the XCTest Target

• Each test case is a method with prefix test

Page 44: Unit Tesing in iOS

XCTest• Provided by Xcode

• New iOS application projects automatically include a unit testing target

• Test classes do not have a header file

• It’s not necessary to add application’s source files to the XCTest Target

• Each test case is a method with prefix test

• Xcode 5 allows to run tests from the editor

Page 45: Unit Tesing in iOS

XCTest Flow

Setup Tear DownTest

- (void)setUp { [super setUp]; // This method is called before the invocation of each test method in the class. } !- (void)tearDown { // This method is called after the invocation of each test method in the class. [super tearDown]; } !- (void)testExample { XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); }

Page 46: Unit Tesing in iOS

Test main actions:• Arrange objects, creating and setting them up as necessary.

• Act on an object.

• Assert that something is as expected.

!- (void)testMakeConversionWithModeMilesToKilometers { // Arrange sut.value = @(1); // Act [sut makeConversionWithMode:ConverterModeMilesToKm]; // Assert XCTAssertTrue([sut.text isEqualToString:@"1.609344"], @"should convert from miles to kilometers"); }

Page 47: Unit Tesing in iOS

@interface ConverterModelTests : XCTestCase { ConverterModel* sut; } @end !@implementation ConverterModelTests !- (void)setUp { [super setUp]; sut = [ConverterModel new]; } !- (void)tearDown { sut = nil; [super tearDown]; } !- (void)testMakeConversionWithModeMilesToKilometers { // Arrange sut.value = @(1); // Act [sut makeConversionWithMode:ConverterModeMilesToKm]; // Assert XCTAssertTrue([sut.text isEqualToString:@"1.609344"], @"should convert from miles to kilometers"); } !@end

Page 48: Unit Tesing in iOS

XCTest Asserts• XCTFail (format…)

• XCTAssertNil (a1, format…)

• XCTAssertNotNil (a1, format…)

• XCTAssert (a1, format…)

• XCTAssertTrue (a1, format…)

• XCTAssertFalse (a1, format…)

• XCTAssertEqualObjects (a1, a2, format…)

• XCTAssertEquals (a1, a2, format…)

• XCTAssertEqualsWithAccuracy (a1, a2, accuracy, format…)

• XCTAssertThrows (expression, format…)

• XCTAssertThrowsSpecific (expression, exception, format…)

• XCTAssertThrowsSpecificNamed (expression, exception, name, format…)

• XCTAssertNoThrow (expression, format…)

• XCTAssertNoThrowSpecific (expression, exception, format…)

• XCTAssertNoThrowSpecificNamed (expression, exception, name, format…)

Page 49: Unit Tesing in iOS

- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; }

Page 50: Unit Tesing in iOS

- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; }

- (void)testViewDidLoadAddsObserverForSomeNotification { // Arrange // Act // Assert !}

Page 51: Unit Tesing in iOS

Dependency Injection

Page 52: Unit Tesing in iOS

Dependency Injection

is a software design pattern that implements passing a service to a client, rather than allowing a client to build or find the service.

Page 53: Unit Tesing in iOS

Constructor Injection

the dependencies are provided through a class constructor

- (id)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter

Page 54: Unit Tesing in iOS

Setter Injection

the client exposes a setter method that the injector uses to inject the dependency

@property (nonatomic, weak) NSUserDefaults* userDefaults;

Page 55: Unit Tesing in iOS

Dependency Injection

providing an instance variable for dependency

Page 56: Unit Tesing in iOS

- (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; }

Dependency

- (void)testViewDidLoadAddsObserverForSomeNotification { // Arrange // Act // Assert !}

Page 57: Unit Tesing in iOS

- (id)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter { self = [super init]; if(self) { _notificationCenter = notificationCenter; } return self; } !!- (void)viewDidLoad { [super viewDidLoad]; [_notificationCenter addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; }

Inject Dependency

Page 58: Unit Tesing in iOS

Mocks

Page 59: Unit Tesing in iOS

is a fake object in the system that verifies the object under test interacted as expected with the fake object.

Mock Object

Page 60: Unit Tesing in iOS

It is common in unit tests to mock or stub collaborators of the system under test so that the test is independent of the implementation of the collaborators.

Mock Object

SUTBehavior Verification

Setup

Exercise

Verify

Tear Down

Behavior (Indirect Outputs)

Fake

Verify

Page 61: Unit Tesing in iOS

Test

Mocks

SUT communicates with the mock object, and all communication is recorded in the mock. The test uses the mock to verify that the test passes.

SUT MockCommunicate

Assert

Page 62: Unit Tesing in iOS

Test

Stubs

Returns specified result for a message, stubs can’t fail the test.

SUT StubCommunicate

Assert

Page 63: Unit Tesing in iOS

@interface ConverterViewController : UIViewController !@property (nonatomic, weak) NSNotificationCenter* notificationCenter; !@end !!@implementation ConverterViewController !- (void)viewDidLoad { [super viewDidLoad]; [_notificationCenter addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; } !@end

Injected Dependency

Page 64: Unit Tesing in iOS

@interface ConverterViewController : UIViewController !@property (nonatomic, weak) NSNotificationCenter* notificationCenter; !@end !!@implementation ConverterViewController !- (void)viewDidLoad { [super viewDidLoad]; [_notificationCenter addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; } !@end

Injected Dependency

Replace with a mock

Page 65: Unit Tesing in iOS

What we need?

•an object that responds to addObserver:selector:name:object:

•possibility to record a call

•check for notification’s name

•verification

Page 66: Unit Tesing in iOS

@interface FakeNotificationCenter : NSObject { BOOL _hasCalled; } @property (nonatomic, strong) NSString* expectedName; !- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject; - (void)verify; !@end !!@implementation FakeNotificationCenter !- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject { _hasCalled = [_expectedName isEqualToString:aName]; } !- (void)verify { NSAssert(_hasCalled, @"expected method was not called with specified parameters"); } !@end

What we can do

Page 67: Unit Tesing in iOS

- (void)testViewDidLoadAddsObserverForSomeNotification { // Arrange FakeNotificationCenter* fake = [FakeNotificationCenter new]; fake.expectedName = kSomeNotification; sut.notificationCenter = (id)fake; // Assert [fake verify]; } !

How can we use it?

Page 68: Unit Tesing in iOS

- (void)testViewDidLoadAddsObserverForSomeNotification { // Arrange FakeNotificationCenter* fake = [FakeNotificationCenter new]; fake.expectedName = kSomeNotification; sut.notificationCenter = (id)fake; ! // Assert [fake verify]; } !-[ConverterViewControllerTests testViewDidLoadAddsObserver] failed: expected method was not called with specified parameters

How can we use it?

Page 69: Unit Tesing in iOS

- (void)testViewDidLoadAddsObserverForSomeNotification { // Arrange FakeNotificationCenter* fake = [FakeNotificationCenter new]; fake.expectedName = kSomeNotification; sut.notificationCenter = (id)fake; ! // Act [sut viewDidLoad]; // Assert [fake verify]; }

How can we use it?

Page 70: Unit Tesing in iOS

OCMock

Page 71: Unit Tesing in iOS

OCMock Provides

•stub objects that return pre-determined values for specific method invocations

•dynamic mocks that can be used to verify interaction patterns

•partial mocks to overwrite selected methods of existing objects

Page 72: Unit Tesing in iOS

Mocks// Creates a mock object that can be used as if it were an instance of // SomeClass. id mock = [OCMockObject mockForClass:[SomeClass class]]; !!// Tells the mock object that someMethod: should be called with an argument // that is equal to someArgument. [[mock expect] someMethod:someArgument]; !// After this setup the functionality under test should be invoked // followed by [mock verify]; !!// When a method is called on a mock object that has not been set up with // either expect or stub the mock object will raise an exception. This // fail-fast mode can be turned off by creating a "nice" mock: id mock = [OCMockObject niceMockForClass:[SomeClass class]]; !// While nice mocks will simply ignore all unexpected methods it is // possible to disallow specific methods: [[mock reject] someMethod];

Page 73: Unit Tesing in iOS

Stubs // Tells the mock object that when someMethod: is called with // someArgument it should return aValue.

[[[mock stub] andReturn:aValue] someMethod:someArgument]; !!! // It is not possible to pass primitive types directly. [[[mock stub] andReturnValue:@YES] aMethodReturnABoolean:someArgument]; !! // The mock object can also throw an exception or post a notification // when a method is called [[[mock stub] andThrow:anException] someMethod:someArgument]; [[[mock stub] andPost:aNotification] someMethod:someArgument]; !!! // If Objective-C blocks are available a block can be used to handle the // invocation and set up a return value void (^theBlock)(NSInvocation *) = ^(NSInvocation *invocation) { /* code that reads and modifies the invocation object */ }; [[[mock stub] andDo:theBlock] someMethod:[OCMArg any]];

Page 74: Unit Tesing in iOS

@interface ConverterViewController : UIViewController !@property (nonatomic, weak) NSNotificationCenter* notificationCenter; !@end !!@implementation ConverterViewController !- (void)viewDidLoad { [super viewDidLoad]; [_notificationCenter addObserver:self selector:@selector(someAction:) name:kSomeNotification object:nil]; } !@end

Injected Dependency

Page 75: Unit Tesing in iOS

- (void)testViewDidLoadAddsObserver { // Arrange id mock = [OCMockObject mockForClass:[NSNotificationCenter class]]; [[mock expect] addObserver:sut selector:[OCMArg anySelector] name:kConverterModelDidUpdateNotification object:OCMOCK_ANY]; sut.notificationCenter = mock; ! // Act [sut viewDidLoad]; // Assert [mock verify]; }

How we test this with OCMock

Page 76: Unit Tesing in iOS

Legacy Code?

Page 77: Unit Tesing in iOS

How to start with Existing Project

• All developers in team must write and run tests

Page 78: Unit Tesing in iOS

How to start with Existing Project

• All developers in team must write and run tests

• Start with warnings and bugs

Page 79: Unit Tesing in iOS

How to start with Existing Project

• All developers in team must write and run tests

• Start with warnings and bugs

• Isolate modules and classes

Page 80: Unit Tesing in iOS

How to start with Existing Project

• All developers in team must write and run tests

• Start with warnings and bugs

• Isolate modules and classes

• Don’t miss refactoring

Page 81: Unit Tesing in iOS

How to start with Existing Project

• All developers in team must write and run tests

• Start with warnings and bugs

• Isolate modules and classes

• Don’t miss refactoring

• Increase test-coverage step-by-step with new

functionality

Page 82: Unit Tesing in iOS

Demo Code

https://github.com/maksumko/ConverterApp

!

maksum.ko

Page 83: Unit Tesing in iOS

Sources

http://www.amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274

!

http://www.amazon.com/Test-Driven-iOS-Development-Developers-Library/dp/0321774183

!

http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627

!

http://martinfowler.com/articles/mocksArentStubs.html

Page 84: Unit Tesing in iOS

Questions?