Unit Testing en iOS
       @hpique
Así terminan los programadores que no hacen unit testing
Agenda

• Unit Testing
• OCUnit
• GHUnit
• Profit!
Unit Testing

Testeo de unidades mínimas de
   código mediante código
- (void)testColorFromHex {
    NSString* colorString = @"#d34f01";

    UIColor* color = [colorString colorFromHex];

    const CGFloat *c = CGColorGetComponents(color.CGColor);
    STAssertEquals(c[0], 211/255.0f, colorString, @"");
    STAssertEquals(c[1], 79/255.0f, colorString, @"");
    STAssertEquals(c[2], 1/255.0f, colorString, @"");
    STAssertEquals(CGColorGetAlpha(color.CGColor), 1.0f,
colorString, @"");
}
Razones para no hacer
     unit testing
...
RazonesExcusas para
no hacer unit testing
Excusas
Excusas

• “No lo necesito”
Excusas

• “No lo necesito”
• “El plazo es muy ajustado”
Excusas

• “No lo necesito”
• “El plazo es muy ajustado”
• “No aplica para este proyecto”
Excusas

• “No lo necesito”
• “El plazo es muy ajustado”
• “No aplica para este proyecto”
• “Unit testing en XCode apesta”
Razones para sí hacer
    unit testing
Razones para sí hacer
    unit testing
• Corregir bugs antes
Razones para sí hacer
    unit testing
• Corregir bugs antes
• Refinar el diseño
Razones para sí hacer
    unit testing
• Corregir bugs antes
• Refinar el diseño
• Facilitar cambios
Razones para sí hacer
    unit testing
• Corregir bugs antes
• Refinar el diseño
• Facilitar cambios
• Documentación útil
Razones para sí hacer
    unit testing
• Corregir bugs antes
• Refinar el diseño
• Facilitar cambios
• Documentación útil
• Reducir tiempo de testeo
¿Cuándo?
¿Cuándo?

• Lo antes posible (TDD)
¿Cuándo?

• Lo antes posible (TDD)
• En paralelo
¿Cuándo?

• Lo antes posible (TDD)
• En paralelo
• Al corregir bugs
Definiciones
            Test Suite

             SetUp

Test Case   Test Case    Test Case

            TearDown
Agenda

• Unit Testing
• OCUnit
• GHUnit
• Profit!
OCUnit


• Framework de Unit Testing para Obj-C
• Único framework de testeo integrado
  nativamente en XCode
+N
#import <SenTestingKit/SenTestingKit.h>

@interface HelloWorldTests : SenTestCase

@end

@implementation HelloWorldTests

- (void)setUp
{
    [super setUp];

    // Set-up code here.
}

- (void)tearDown
{
    // Tear-down code here.

       [super tearDown];
}

- (void)testExample
{
    STFail(@"Unit tests are not implemented yet in HelloWorldTests");
}

@end
Escribiendo unit tests
     con OCUnit
• Cada Test Suite es una clase que hereda de
  SenTestCase

• Cada Test Case debe ser un método con el
  prefijo test

• setUp y tearDown son opcionales
+U
Console output
2011-12-09 12:43:01.394 HelloWorld[2858:fb03] Applications are expected to have a
root view controller at the end of application launch
Test Suite 'All tests' started at 2011-12-09 11:43:01 +0000
Test Suite '/Users/hermespique/Library/Developer/Xcode/DerivedData/HelloWorld-
ezilismrgmrzecbbsndapbyeczre/Build/Products/Debug-iphonesimulator/
HelloWorldTests.octest(Tests)' started at 2011-12-09 11:43:01 +0000
Test Suite 'HelloWorldTests' started at 2011-12-09 11:43:01 +0000
Test Case '-[HelloWorldTests testExample]' started.
/Users/hermespique/Documents/workspace/HelloWorld/HelloWorldTests/
HelloWorldTests.m:33: error: -[HelloWorldTests testExample] : Unit tests are not
implemented yet in HelloWorldTests
Test Case '-[HelloWorldTests testExample]' failed (0.000 seconds).
Test Suite 'HelloWorldTests' finished at 2011-12-09 11:43:01 +0000.
Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds
Test Suite '/Users/hermespique/Library/Developer/Xcode/DerivedData/HelloWorld-
ezilismrgmrzecbbsndapbyeczre/Build/Products/Debug-iphonesimulator/
HelloWorldTests.octest(Tests)' finished at 2011-12-09 11:43:01 +0000.
Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds
Test Suite 'All tests' finished at 2011-12-09 11:43:01 +0000.
Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.001) seconds
OCUnit Macros
STAssertEqualObjects(a1, a2, description, ...)
STAssertEquals(a1, a2, description, ...)
STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...)
STFail(description, ...)
STAssertNil(a1, description, ...)
STAssertNotNil(a1, description, ...)
STAssertTrue(expr, description, ...)
STAssertTrueNoThrow(expr, description, ...)
STAssertFalse(expr, description, ...)
STAssertFalseNoThrow(expr, description, ...)
STAssertThrows(expr, description, ...)
STAssertThrowsSpecific(expr, specificException, description, ...)
STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...)
STAssertNoThrow(expr, description, ...)
STAssertNoThrowSpecific(expr, specificException, description, ...)
STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...)
OCUnit Macros
STAssertTrue(expr, description, ...)

STAssertFalse(expr, description, ...)

STAssertNil(a1, description, ...)

STAssertNotNil(a1, description, ...)

STAssertEqualObjects(a1, a2, description, ...)

STAssertEquals(a1, a2, description, ...)

STFail(description, ...)

STAssertThrows(expr, description, ...)
Agenda

• Unit Testing
• OCUnit
• GHUnit
• Profit!
GHUnit

• Framework de Unit Testing para Obj-C
• Open-source: github.com/gabriel/gh-unit
• GUI!
• Compatible con OCUnit
#import <GHUnitIOS/GHUnit.h>

@interface ExampleTest : GHTestCase
@end

@implementation ExampleTest

- (BOOL)shouldRunOnMainThread {
    return NO;
}

- (void)setUpClass {
    // Run at start of all tests in the class
}

- (void)setUp {
    // Run before each test method
}

- (void)tearDown {
    // Run after each test method
}

- (void)tearDownClass {
    // Run at end of all tests in the class
}

- (void)testFoo {
    NSString *a = @"foo";
    GHAssertNotNil(a, nil);
}

@end
GHUnit Macros
GHAssertNoErr(a1, description, ...)           GHAssertEquals(a1, a2, description, ...)
GHAssertErr(a1, a2, description, ...)         GHAbsoluteDifference(left,right)
GHAssertNotNULL(a1, description, ...)         (MAX(left,right)-MIN(left,right))
GHAssertNULL(a1, description, ...)            GHAssertEqualsWithAccuracy(a1, a2,
GHAssertNotEquals(a1, a2, description, ...)   accuracy, description, ...)
GHAssertNotEqualObjects(a1, a2, desc, ...)    GHFail(description, ...)
GHAssertOperation(a1, a2, op,                 GHAssertNil(a1, description, ...)
description, ...)                             GHAssertNotNil(a1, description, ...)
GHAssertGreaterThan(a1, a2,                   GHAssertTrue(expr, description, ...)
description, ...)                             GHAssertTrueNoThrow(expr,
GHAssertGreaterThanOrEqual(a1, a2,            description, ...)
description, ...)                             GHAssertFalse(expr, description, ...)
GHAssertLessThan(a1, a2, description, ...)    GHAssertFalseNoThrow(expr,
GHAssertLessThanOrEqual(a1, a2,               description, ...)
description, ...)                             GHAssertThrows(expr, description, ...)
GHAssertEqualStrings(a1, a2,                  GHAssertThrowsSpecific(expr,
description, ...)                             specificException, description, ...)
GHAssertNotEqualStrings(a1, a2,               GHAssertThrowsSpecificNamed(expr,
description, ...)                             specificException, aName,
GHAssertEqualCStrings(a1, a2,                 description, ...)
description, ...)                             GHAssertNoThrow(expr, description, ...)
GHAssertNotEqualCStrings(a1, a2,              GHAssertNoThrowSpecific(expr,
description, ...)                             specificException, description, ...)
GHAssertEqualObjects(a1, a2,                  GHAssertNoThrowSpecificNamed(expr,
description, ...)                             specificException, aName,
                                              description, ...)
GHUnitAsyncTestCase
#import <GHUnitIOS/GHUnit.h>

@interface AsyncTest : GHAsyncTestCase { }
@end

@implementation AsyncTest

- (void)testURLConnection {
    [self prepare];

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://
www.google.com"]];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self startImmediately:YES];

       [self waitForStatus:kGHUnitWaitStatusSuccess timeout:10.0];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testURLConnection)];
}

@end
Configurar GHUnit

1. Crear target
2. Agregar GHUnitiOS.framework
3. Modificar Other Linker Flags
4. Cambiar de AppDelegate
1. Crear target
1. Crear target
2. Agregar
GHUnitiOS.framework
• Primero debemos hacer build del framework
1. Descargar de github y descomprimir
2. > cd gh-unit/Project-iOS
3. > make
3. Modificar Other Link
         Flags

• Agregar -all_load
• Agregar -objC
4. Cambiar de
          AppDelegate
int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc,
argv, nil, @"GHUnitIOSAppDelegate");
    }
}
+B
OCUnit vs GHUnit
                    OCUnit           GHUnit
Integración con
                    Built-in          Manual
    XCode
                  Contextual /
  Resultados                      Consola / GUI
                   Consola
                                   Más macros
Programación
                                 GHAsyncTestCase
                                 Todo, selección o
  Ejecución          Todo
                                     fallidos
¡Gracias!


  @hpique

Unit testing en iOS @ MobileCon Galicia

  • 1.
    Unit Testing eniOS @hpique
  • 2.
    Así terminan losprogramadores que no hacen unit testing
  • 3.
    Agenda • Unit Testing •OCUnit • GHUnit • Profit!
  • 4.
    Unit Testing Testeo deunidades mínimas de código mediante código
  • 5.
    - (void)testColorFromHex { NSString* colorString = @"#d34f01"; UIColor* color = [colorString colorFromHex]; const CGFloat *c = CGColorGetComponents(color.CGColor); STAssertEquals(c[0], 211/255.0f, colorString, @""); STAssertEquals(c[1], 79/255.0f, colorString, @""); STAssertEquals(c[2], 1/255.0f, colorString, @""); STAssertEquals(CGColorGetAlpha(color.CGColor), 1.0f, colorString, @""); }
  • 6.
    Razones para nohacer unit testing
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    Excusas • “No lonecesito” • “El plazo es muy ajustado”
  • 12.
    Excusas • “No lonecesito” • “El plazo es muy ajustado” • “No aplica para este proyecto”
  • 13.
    Excusas • “No lonecesito” • “El plazo es muy ajustado” • “No aplica para este proyecto” • “Unit testing en XCode apesta”
  • 14.
    Razones para síhacer unit testing
  • 15.
    Razones para síhacer unit testing • Corregir bugs antes
  • 16.
    Razones para síhacer unit testing • Corregir bugs antes • Refinar el diseño
  • 17.
    Razones para síhacer unit testing • Corregir bugs antes • Refinar el diseño • Facilitar cambios
  • 18.
    Razones para síhacer unit testing • Corregir bugs antes • Refinar el diseño • Facilitar cambios • Documentación útil
  • 19.
    Razones para síhacer unit testing • Corregir bugs antes • Refinar el diseño • Facilitar cambios • Documentación útil • Reducir tiempo de testeo
  • 20.
  • 21.
  • 22.
    ¿Cuándo? • Lo antesposible (TDD) • En paralelo
  • 23.
    ¿Cuándo? • Lo antesposible (TDD) • En paralelo • Al corregir bugs
  • 24.
    Definiciones Test Suite SetUp Test Case Test Case Test Case TearDown
  • 25.
    Agenda • Unit Testing •OCUnit • GHUnit • Profit!
  • 26.
    OCUnit • Framework deUnit Testing para Obj-C • Único framework de testeo integrado nativamente en XCode
  • 29.
  • 30.
    #import <SenTestingKit/SenTestingKit.h> @interface HelloWorldTests: SenTestCase @end @implementation HelloWorldTests - (void)setUp { [super setUp]; // Set-up code here. } - (void)tearDown { // Tear-down code here. [super tearDown]; } - (void)testExample { STFail(@"Unit tests are not implemented yet in HelloWorldTests"); } @end
  • 31.
    Escribiendo unit tests con OCUnit • Cada Test Suite es una clase que hereda de SenTestCase • Cada Test Case debe ser un método con el prefijo test • setUp y tearDown son opcionales
  • 32.
  • 33.
    Console output 2011-12-09 12:43:01.394HelloWorld[2858:fb03] Applications are expected to have a root view controller at the end of application launch Test Suite 'All tests' started at 2011-12-09 11:43:01 +0000 Test Suite '/Users/hermespique/Library/Developer/Xcode/DerivedData/HelloWorld- ezilismrgmrzecbbsndapbyeczre/Build/Products/Debug-iphonesimulator/ HelloWorldTests.octest(Tests)' started at 2011-12-09 11:43:01 +0000 Test Suite 'HelloWorldTests' started at 2011-12-09 11:43:01 +0000 Test Case '-[HelloWorldTests testExample]' started. /Users/hermespique/Documents/workspace/HelloWorld/HelloWorldTests/ HelloWorldTests.m:33: error: -[HelloWorldTests testExample] : Unit tests are not implemented yet in HelloWorldTests Test Case '-[HelloWorldTests testExample]' failed (0.000 seconds). Test Suite 'HelloWorldTests' finished at 2011-12-09 11:43:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds Test Suite '/Users/hermespique/Library/Developer/Xcode/DerivedData/HelloWorld- ezilismrgmrzecbbsndapbyeczre/Build/Products/Debug-iphonesimulator/ HelloWorldTests.octest(Tests)' finished at 2011-12-09 11:43:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.000) seconds Test Suite 'All tests' finished at 2011-12-09 11:43:01 +0000. Executed 1 test, with 1 failure (0 unexpected) in 0.000 (0.001) seconds
  • 34.
    OCUnit Macros STAssertEqualObjects(a1, a2,description, ...) STAssertEquals(a1, a2, description, ...) STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) STFail(description, ...) STAssertNil(a1, description, ...) STAssertNotNil(a1, description, ...) STAssertTrue(expr, description, ...) STAssertTrueNoThrow(expr, description, ...) STAssertFalse(expr, description, ...) STAssertFalseNoThrow(expr, description, ...) STAssertThrows(expr, description, ...) STAssertThrowsSpecific(expr, specificException, description, ...) STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) STAssertNoThrow(expr, description, ...) STAssertNoThrowSpecific(expr, specificException, description, ...) STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...)
  • 35.
    OCUnit Macros STAssertTrue(expr, description,...) STAssertFalse(expr, description, ...) STAssertNil(a1, description, ...) STAssertNotNil(a1, description, ...) STAssertEqualObjects(a1, a2, description, ...) STAssertEquals(a1, a2, description, ...) STFail(description, ...) STAssertThrows(expr, description, ...)
  • 36.
    Agenda • Unit Testing •OCUnit • GHUnit • Profit!
  • 37.
    GHUnit • Framework deUnit Testing para Obj-C • Open-source: github.com/gabriel/gh-unit • GUI! • Compatible con OCUnit
  • 39.
    #import <GHUnitIOS/GHUnit.h> @interface ExampleTest: GHTestCase @end @implementation ExampleTest - (BOOL)shouldRunOnMainThread { return NO; } - (void)setUpClass { // Run at start of all tests in the class } - (void)setUp { // Run before each test method } - (void)tearDown { // Run after each test method } - (void)tearDownClass { // Run at end of all tests in the class } - (void)testFoo { NSString *a = @"foo"; GHAssertNotNil(a, nil); } @end
  • 40.
    GHUnit Macros GHAssertNoErr(a1, description,...) GHAssertEquals(a1, a2, description, ...) GHAssertErr(a1, a2, description, ...) GHAbsoluteDifference(left,right) GHAssertNotNULL(a1, description, ...) (MAX(left,right)-MIN(left,right)) GHAssertNULL(a1, description, ...) GHAssertEqualsWithAccuracy(a1, a2, GHAssertNotEquals(a1, a2, description, ...) accuracy, description, ...) GHAssertNotEqualObjects(a1, a2, desc, ...) GHFail(description, ...) GHAssertOperation(a1, a2, op, GHAssertNil(a1, description, ...) description, ...) GHAssertNotNil(a1, description, ...) GHAssertGreaterThan(a1, a2, GHAssertTrue(expr, description, ...) description, ...) GHAssertTrueNoThrow(expr, GHAssertGreaterThanOrEqual(a1, a2, description, ...) description, ...) GHAssertFalse(expr, description, ...) GHAssertLessThan(a1, a2, description, ...) GHAssertFalseNoThrow(expr, GHAssertLessThanOrEqual(a1, a2, description, ...) description, ...) GHAssertThrows(expr, description, ...) GHAssertEqualStrings(a1, a2, GHAssertThrowsSpecific(expr, description, ...) specificException, description, ...) GHAssertNotEqualStrings(a1, a2, GHAssertThrowsSpecificNamed(expr, description, ...) specificException, aName, GHAssertEqualCStrings(a1, a2, description, ...) description, ...) GHAssertNoThrow(expr, description, ...) GHAssertNotEqualCStrings(a1, a2, GHAssertNoThrowSpecific(expr, description, ...) specificException, description, ...) GHAssertEqualObjects(a1, a2, GHAssertNoThrowSpecificNamed(expr, description, ...) specificException, aName, description, ...)
  • 41.
    GHUnitAsyncTestCase #import <GHUnitIOS/GHUnit.h> @interface AsyncTest: GHAsyncTestCase { } @end @implementation AsyncTest - (void)testURLConnection { [self prepare]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http:// www.google.com"]]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; [self waitForStatus:kGHUnitWaitStatusSuccess timeout:10.0]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testURLConnection)]; } @end
  • 42.
    Configurar GHUnit 1. Creartarget 2. Agregar GHUnitiOS.framework 3. Modificar Other Linker Flags 4. Cambiar de AppDelegate
  • 43.
  • 44.
  • 45.
    2. Agregar GHUnitiOS.framework • Primerodebemos hacer build del framework 1. Descargar de github y descomprimir 2. > cd gh-unit/Project-iOS 3. > make
  • 46.
    3. Modificar OtherLink Flags • Agregar -all_load • Agregar -objC
  • 47.
    4. Cambiar de AppDelegate int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate"); } }
  • 48.
  • 49.
    OCUnit vs GHUnit OCUnit GHUnit Integración con Built-in Manual XCode Contextual / Resultados Consola / GUI Consola Más macros Programación GHAsyncTestCase Todo, selección o Ejecución Todo fallidos
  • 50.