bienvenido a la republica independiente de las pruebas unitarias con core data

61
Bienvenido a la república independiente de las pruebas unitarias con Core Data Jorge D. Ortiz Fuentes (@jdortiz) Alfonso Alba Garcia (@aalbagarcia) viernes, 8 de marzo de 13

Upload: alfonso-alba

Post on 13-Jan-2015

1.251 views

Category:

Technology


0 download

DESCRIPTION

Presentación para el NSCoder Night Madrid del 6 de marzo de 2013 por Jorge Ortiz y yo mismo. En esta presentación hablado de cómo se pueden hacer pruebas unitarias a un modelo en Core Data y cómo conseguir desacoplar este modelo de la forma en que hemos elegido que persista.

TRANSCRIPT

Page 1: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Bienvenido a la república independiente de las pruebas

unitarias con Core DataJorge D. Ortiz Fuentes (@jdortiz)

Alfonso Alba Garcia (@aalbagarcia)

viernes, 8 de marzo de 13

Page 2: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Agenda★MVC

★Implementación en Core Data

★Pruebas Unitarias

★Desacoplamiento

★Conclusiones

2

viernes, 8 de marzo de 13

Page 3: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

El modelo a seguirviernes, 8 de marzo de 13

Page 4: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

MVC★ Las vistas las proporciona Apple (aunque

nosotros podemos crear lasque necesitemos).★ El modelo debe contener toda la lógica de

negocio.★ Atención: Modelo de datos vs modelo de

negocio.★ El controlador debería la conexión de las

vistas con el modelo de negocio.★ No es necesario que sea / NO debería ser un

singleton. Se pasa de un controlador a otro. (Core Data: MOC o UIManagedDocument)

4

viernes, 8 de marzo de 13

Page 5: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Modelo autocontenido★ La forma más sencilla de evitar

duplicación de código y conseguir un comportamiento consistente.

★ Core Data incluye restricciones. P. ej., atributo opcional o no o cardinalidad de una relación.

★ Pero para añadir otra funcionalidad hay que añadir métodos.

5

viernes, 8 de marzo de 13

Page 6: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Usa Core Data, Lukeviernes, 8 de marzo de 13

Page 7: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Implementación del modelo de negocio★ Modificar modelo de datos ⇒

regenerar clases. Xcode sobreescribe ⇒

★ Soluciones:๏ Aprovechar el control de versiones

๏ mogenerator (http://rentzsch.github.com/mogenerator/) de W. Rentzsch

๏ Categorías

7

métodos añadidos

viernes, 8 de marzo de 13

Page 8: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Implementación del modelo de negocio★ Modificar modelo de datos ⇒

regenerar clases. Xcode sobreescribe ⇒

★ Soluciones:๏ Aprovechar el control de versiones

๏ mogenerator (http://rentzsch.github.com/mogenerator/) de W. Rentzsch

๏ Categorías

7

métodos añadidos

viernes, 8 de marzo de 13

Page 9: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Modelo NSCoderApp

viernes, 8 de marzo de 13

Page 10: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Group (Generado)★ #import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

@class Person;

@interface Group : NSManagedObject

@property (nonatomic, retain) NSString * name;@property (nonatomic, retain) NSString * notes;@property (nonatomic, retain) NSSet *members;@property (nonatomic, retain) NSManagedObject *meetings;@end

@interface Group (CoreDataGeneratedAccessors)

- (void)addMembersObject:(Person *)value;- (void)removeMembersObject:(Person *)value;- (void)addMembers:(NSSet *)values;- (void)removeMembers:(NSSet *)values;

@end

#import "Group.h"#import "Person.h"

@implementation Group

@dynamic name;@dynamic notes;@dynamic members;@dynamic meetings;

@end

viernes, 8 de marzo de 13

Page 11: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Categoría Group+Model★ #import "Group.h"

@interface Group (Model)

- (BOOL) isDuplicated;

@end

#import "Group+Model.h"

@implementation Group (Model)

#pragma mark - Detect duplicates

/** Verify that this item doesn't exist yet (another section with the same name). */- (BOOL) isDuplicated { BOOL duplicated = NO; NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Group"];

// Set the predicate to find if another one exists. fetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", self.name];

NSError *error = nil; NSUInteger sections = [self.managedObjectContext countForFetchRequest:fetchRequest error:&error]; // The first one is the one this one. if (sections > 1) { duplicated = YES; }

return duplicated;}

@end

viernes, 8 de marzo de 13

Page 12: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Pruebasviernes, 8 de marzo de 13

Page 13: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

La prueba del 3★ Incluir pruebas:๏ Más exhaustivo y sistemático.

๏ Refactorizar con mucho menos riesgo.

๏ Verificar la resolución de bugs y evitar regresiones.

★ Lo que proporciona el sistema (frameworks) no se prueba.

★ Como mínimo:๏ Modelo de negocio → Métodos añadidos

12

viernes, 8 de marzo de 13

Page 14: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Implementación de las pruebas★ OCUnit para no compicarse la vida★ Nombre explicativo★ Cobertura★ Casos relevantes★ Importante para Core Data: setUp y

tearDown๏ Carga del modelo

๏ Preparación del Persistent Store en memoria.

13

viernes, 8 de marzo de 13

Page 15: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Preparación★ - (void) setUp {

[super setUp]; // Create the Core Data stack. NSBundle *bundle = [NSBundle bundleForClass:[self class]]; model = [NSManagedObjectModel mergedModelFromBundles:@[bundle]]; coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; store = [coordinator addPersistentStoreWithType: NSInMemoryStoreType configuration: nil URL: nil options: nil error: NULL]; context = [[NSManagedObjectContext alloc] init]; [context setPersistentStoreCoordinator:coordinator];

// Instantiate three products for the tests. mainGroup = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context]; mainGroup.name = @"A cool group";}

- (void) tearDown { context = nil; store = nil; coordinator = nil; model = nil;

[super tearDown];}

14

viernes, 8 de marzo de 13

Page 16: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

No duplicación

★ #pragma mark - Detect duplication

- (void) testDuplicatedIsDetectedWhenTwoGroupsWithSameName { Group *anotherGroup = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context]; anotherGroup.name = @"A cool group"; STAssertTrue([mainGroup isDuplicated], @"If another group exists with the same name it is considered a duplicate");}

- (void) testDuplicatedIsNotDetectedWhenTwoGroupsWithDifferentName { Store *anotherStore = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:context]; anotherGroup.name = @"Another cool group"; STAssertFalse([mainGroup isDuplicated], @"If no other group exists with a different name it is not considered a duplicate");}

15

viernes, 8 de marzo de 13

Page 17: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Desacoplamientoviernes, 8 de marzo de 13

Page 18: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

This is an open discussion

viernes, 8 de marzo de 13

Page 19: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Marco Arment

“It’s so simple, I’ll just use plist”

viernes, 8 de marzo de 13

Page 20: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

El problema★ Acoplamiento fuerte entre los

componentes de la aplicación (Models, Views, Controllers)

★ Difícil hacer mocks para aplicar TDD๏ TDD = componentes independientes

que se testan de forma independiente entre sí

๏ (¿Mocking de NSManagedObjectContext?)

19

viernes, 8 de marzo de 13

Page 21: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

El problema★ Para testear un View Controller que

muestra en pantalla un listado de Meetings tendría que:๏ Crear un NSManagedObjectContext

๏ Cargar datos de prueba en la base de datos

๏ Hacer una búsqueda sobre los datos de prueba

๏ Generar un NSFetchedResultsController

๏ ...y finalmente testear

20

viernes, 8 de marzo de 13

Page 22: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

El problema★ ...y después de hacer todo

esto, resulta que queremos hacer una versión para Android y compartir datos usando Parse/RoR/PHP/DynamoDB

★ ...o prefieres usar plists para no complicarte la vida (como Marco Arment, pero al revés)

21

viernes, 8 de marzo de 13

Page 23: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

El problema

http://www.confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years

viernes, 8 de marzo de 13

Page 24: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

“The database is a DETAIL of our application”

viernes, 8 de marzo de 13

Page 25: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Model(Core Data)

View

Meeting.h

viernes, 8 de marzo de 13

Page 26: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Como la base de datos es un detalle, la podemos quitar

viernes, 8 de marzo de 13

Page 27: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

viernes, 8 de marzo de 13

Page 28: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

Entidad: Objeto que contiene las reglas de negocio genéricas, aquellas que se pueden aplicar siempre en cualquier contexto.

Por ejemplo: Si a un evento se inscriben tres personas, este queda automáticamente confirmado

MeetingEntity

GroupEntity

AttendeeEntity

viernes, 8 de marzo de 13

Page 29: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

Interactor: Contiene reglas de negocio específicas

Por ejemplo: Dos eventos no pueden tener el mismo nombre

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

viernes, 8 de marzo de 13

Page 30: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Y la base de datos?

viernes, 8 de marzo de 13

Page 31: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

viernes, 8 de marzo de 13

Page 32: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

Request Object

viernes, 8 de marzo de 13

Page 33: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

Request Object

viernes, 8 de marzo de 13

Page 34: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

Request Object

...accede a Core Data y busca los objetos que cumplen los criterios del RequestObject

viernes, 8 de marzo de 13

Page 35: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

Entidades

viernes, 8 de marzo de 13

Page 36: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

Entidades

ResponseObject

viernes, 8 de marzo de 13

Page 37: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsGateway

Model(Core Data)

ResponseObject

View

viernes, 8 de marzo de 13

Page 38: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

show me the code!!

viernes, 8 de marzo de 13

Page 39: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Duck Typing

viernes, 8 de marzo de 13

Page 40: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsParseGateway

Model(Core Data)

ResponseObject

View

RequestObjectProtocol

ResponseObject

viernes, 8 de marzo de 13

Page 41: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

QueryRequestProtocol

@protocol QueryRequestProtocol

@required@property (nonatomic, strong) NSString *queryString;@property (nonatomic, strong, readonly) NSDictionary *components;

@end

viernes, 8 de marzo de 13

Page 42: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

BrowseMeetingsInteractor

@interface BrowseMeetingsInteractor : NSObject

@property (nonatomic, strong) id<MeetingGatewayProtocol> gateway;

- (id<StandardResponseProtocol>) getResponseForRequest:(id<QueryRequestProtocol >)request;@end

viernes, 8 de marzo de 13

Page 43: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

MeetingGateway

@class MeetingEntity;

@protocol MeetingGatewayProtocol <NSObject>

@required- (id)processRequest:(id<QueryRequestProtocol> *)request;- (void)save:(MeetingEntity *)meeting error:(NSError **)error;@end

viernes, 8 de marzo de 13

Page 44: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

viewDidLoad if (!self.fridgeResponse) { IFCoreDataFridgeGateway *gateway = [[IFCoreDataFridgeGateway alloc] init]; gateway.shouldReturnFetchResultsController = false; gateway.context = self.context;

self.fridgeInteractor = [[IFViewFridgeInteractor alloc] init]; self.fridgeInteractor.gateway = gateway; self.fridgeResponse = [self.fridgeInteractor getResponseForRequest:self.request]; } if (!self.fridgeItemsResponse) { IFCoreDataFridgeItemGateway *fridgeItemGateway = [[IFCoreDataFridgeItemGateway alloc] init]; fridgeItemGateway.context = self.context; fridgeItemGateway.shouldReturnFetchResultsController = YES; self.fridgeItemsInteractor = [[IFBrowseFridgeItemsInteractor alloc] init]; self.fridgeItemsInteractor.gateway = fridgeItemGateway; IFFridgeItemRequestObject *fridgeItemsRequest = [[IFFridgeItemRequestObject alloc] init]; ; fridgeItemsRequest.queryString = [NSString stringWithFormat:@"fridge-slug=%@",self.request.components[@"slug"] ] ; self.fridgeItemsResponse = [self.fridgeItemsInteractor getResponseForRequest:fridgeItemsRequest]; }

viernes, 8 de marzo de 13

Page 45: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Cómo cambiamos de CoreData a Parse?

viernes, 8 de marzo de 13

Page 46: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsParseGateway

Cache(Core Data)

ResponseObject

View

Parse

Mientras la clase MeetingsParseGateway y

MeetingsGateway cumplan el mismo protocolo ¡todo

funciona!

RequestObject

ResponseObject

viernes, 8 de marzo de 13

Page 47: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¡¡Podemos testearlo todo!!

viernes, 8 de marzo de 13

Page 48: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsParseGateway

Cache(Core Data)

ResponseObject

View

Parse

RequestObject

ResponseObject

viernes, 8 de marzo de 13

Page 49: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

MockBrowseMeetingsInteractor

MockResponseObject

RequestObject

Para testear el view controller, basta con tener un Mock del

Interactor

viernes, 8 de marzo de 13

Page 50: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

View Controller

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MeetingsParseGateway

Cache(Core Data)

ResponseObject

View

Parse

Mientras la clase MeetingsParseGateway y

MeetingsGateway cumplan el mismo protocolo ¡todo

funciona!

RequestObject

ResponseObject

viernes, 8 de marzo de 13

Page 51: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Request:quiero ver todos los

objetos Meeting

MeetingEntity

GroupEntity

AttendeeEntity

BrowseMeetingsInteractor

MockGateway

MockRequestObjectResponseObject

viernes, 8 de marzo de 13

Page 52: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

En la vida nada es gratis...★Hay que escribir más código

★Hay que pensar en interfaces (protocolos)

★¿Rendimiento?

51

viernes, 8 de marzo de 13

Page 53: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?

52

viernes, 8 de marzo de 13

Page 54: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?★ ¿Creéis que tener un sistema de componentes

realmente desacoplados es un buen sistema?

52

viernes, 8 de marzo de 13

Page 55: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?★ ¿Creéis que tener un sistema de componentes

realmente desacoplados es un buen sistema?★ ¿Pensáis que tener un sistema en el que puedo

testear sus componentes por separado, es un buen sistema?

52

viernes, 8 de marzo de 13

Page 56: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?★ ¿Creéis que tener un sistema de componentes

realmente desacoplados es un buen sistema?★ ¿Pensáis que tener un sistema en el que puedo

testear sus componentes por separado, es un buen sistema?

★ ¿Pensáis que tener un sistema flexible en el que puedo posponer las decisiones sobre aspectos fundamentales del mismo hasta que realmente las necesito es un buen sistema?

52

viernes, 8 de marzo de 13

Page 57: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?★ ¿Creéis que tener un sistema de componentes

realmente desacoplados es un buen sistema?★ ¿Pensáis que tener un sistema en el que puedo

testear sus componentes por separado, es un buen sistema?

★ ¿Pensáis que tener un sistema flexible en el que puedo posponer las decisiones sobre aspectos fundamentales del mismo hasta que realmente las necesito es un buen sistema?

★ ¿Pensáis que poder sustituir unas componentes por otras es un buen sistema?

52

viernes, 8 de marzo de 13

Page 58: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¿Qué pensáis?★ ¿Creéis que tener un sistema de componentes

realmente desacoplados es un buen sistema?★ ¿Pensáis que tener un sistema en el que puedo

testear sus componentes por separado, es un buen sistema?

★ ¿Pensáis que tener un sistema flexible en el que puedo posponer las decisiones sobre aspectos fundamentales del mismo hasta que realmente las necesito es un buen sistema?

★ ¿Pensáis que poder sustituir unas componentes por otras es un buen sistema?

★ ¿Pensáis que un sistema en el que una buena parte del código se puede autogenerar o reutilizar es un buen sistema?

52

viernes, 8 de marzo de 13

Page 59: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

Cuando no... ★Si quieres hacer un prototipo rápido de la applicación

★Si no quieres hacer TDD, ni desacoplar componentes ni dormir más tranquilo por las noches cuando tu app se la descarguen miles de personas

53

viernes, 8 de marzo de 13

Page 60: Bienvenido a la republica independiente de las pruebas unitarias con Core Data

¡Es una inversión de futuro!

¡Gracias!

viernes, 8 de marzo de 13