Cómo probar una unidad UIViewController – TDD / BDD

La testing de la unidad es algo que nunca me parece posible, pero puedo ver por qué es importante y puede ser un gran ahorro de time (si sabes lo que estás haciendo). Espero que alguien pueda señalarme en la dirección correcta.

Tengo el siguiente UIViewController

QBElectricityBaseVC.h

 @interface QBElectricityBaseVC : QBStateVC @property (nonatomic, strong) QBElectricityUsage *electricityUsage; @property (nonatomic, assign) CGFloat tabBarHeight; - (void)updateElectricityUsage; @end 

QBElectricityBaseVC.m

 @implementation QBElectricityBaseVC - (instancetype)init { self = [super init]; if (self) { self.tabBarItem = [[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"electricity_title", nil) image:nil tag:0]; } return self; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage) name:kUpdatedElectricityUsageKey object:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.notificationCenter removeObserver:self]; } - (void)updateElectricityUsage { self.electricityUsage = [self.stateManager electricityUsage]; } - (CGFloat)tabBarHeight { return self.tabBarController.tabBar.frame.size.height; } @end 

¿Qué debo probar?

  • Se kUpdatedElectricityUsageKey un observador para kUpdatedElectricityUsageKey
  • self.electricityUsage convierte en una instancia de QBElectricityUsage
  • Se tabBarHeight una tabBarHeight
  • Se kUpdatedElectricityUsageKey un observador para kUpdatedElectricityUsageKey

¿Me falta algo que debería probar o probar algo que realmente no debería?

¿Cómo lo hago?

Entonces, estoy tratando de hacer esto usando Specta y Expexta . Si necesito burlarme de algo, estaría usando OCMockito .

Realmente no sé cómo probar el observador se agrega / elimina. Veo lo siguiente en la documentation de Expexta pero no estoy seguro de si es relevante / cómo usarlo:

 expect(^{ /* code */ }).to.notify(@"NotificationName"); passes if a given block of code generates an NSNotification named NotificationName. expect(^{ /* code */ }).to.notify(notification); passes if a given block of code generates an NSNotification equal to the passed notification. 

Para probar que self.electricityUsage convierte en una instancia de QBElectricityUsage , podría crear una categoría que tenga un método que simplemente finge que la notificación se ha updateElectricityUsage y llama al método updateElectricityUsage pero ¿es esta la mejor manera?

Y en cuanto a tabBarHeight , ¿debo probar que devuelve un CGFloat válido y no preocuparme de cuál es el valor?


ACTUALIZAR

viewWillAppear método viewWillAppear para que se vea a continuación:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self addNotificationObservers]; } - (void)addNotificationObservers { [self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage) name:kUpdatedElectricityUsageKey object:nil]; } 

Y luego creé la siguiente testing:

 #import "Specs.h" #import "QBElectricityBaseVC.h" #import "ElectricityConstants.h" SpecBegin(QBElectricityBaseVCSpec) describe(@"QBElectricityBaseVC", ^{ __block QBElectricityBaseVC *electricityBaseVC; __block NSNotificationCenter *mockNotificationCenter; beforeEach(^{ electricityBaseVC = [QBElectricityBaseVC new]; mockNotificationCenter = mock([NSNotificationCenter class]); electricityBaseVC.notificationCenter = mockNotificationCenter; }); afterEach(^{ electricityBaseVC = nil; mockNotificationCenter = nil; }); it(@"should have a notification observer for updated electricity usage", ^{ [electricityBaseVC addNotificationObservers]; [verify(mockNotificationCenter) addObserver:electricityBaseVC selector:@selector(updateElectricityUsage) name:kUpdatedElectricityUsageKey object:nil]; }); }); SpecEnd 

Esa testing ahora pasa pero ¿es esta la mejor manera de probar esto?

Acabas de sentir una gran image de iOS ViewControllers: chupan la capacidad de testing .

  • ViewControllers mezcla la lógica de administrar la vista y el model
    • Esto lleva a masivos ViewControllers
    • Esto viola la regla de responsabilidad única.
      • Esto hace que el código no sea reutilizable

Otro gran problema con MVC es que desalienta a los desarrolladores a escribir testings unitarias. Dado que los controlleres de vista mezclan la lógica de manipulación de la vista con la lógica empresarial, separar esos componentes en aras de la testing de unidades se convierte en una tarea hercúlea. Una tarea que muchos ignoran en favor de … simplemente no probar nada.

Artículo – fuente

Tal vez deberías pensar en usar MVVM en su lugar. Este es un excelente artículo que explica la diferencia de iOS MVC y MVVM.

Lo bueno de usar MVVM es que puedes usar DataBinding con Reactive Cocoa. Aquí hay un tutorial que explicará el data binding con MVVM y la progtwigción reactiva en iOS.

Sigo 2 prácticas para probar las piezas de un UIViewController.

  1. MVVM: con un patrón MVVM , puede probar fácilmente la unidad de contenido de sus vistas en las testings de su unidad para sus classs ViewModel. Esto también mantiene la lógica de su ViewController muy ligera para que no tenga que escribir tantas testings de UI como para cubrir todos esos escenarios.
  2. KIF: luego para las testings de interfaz de usuario, uso KIF porque su agente de testing ayuda a manejar asíncronos y ver los retrasos en la carga. Con KIF puedo publicar una notificación de mi código y mi testing esperará para ver los efectos de mi manejador de notifications en la vista.

Entre esos 2 sistemas puedo probar prácticamente todo y luego escribir muy fácilmente las testings de interfaz de usuario para cubrir las partes finales.

Además, una nota rápida sobre su código: no agregaría sus observadores en viewWillAppear porque se llama más de una vez. Sin embargo, puede que no sea un problema ya que probablemente no recibirá llamadas networkingundantes a su manejador debido a la fusión de notifications.