2015-10-29 45 views
5

Ho un servizio ember che è di primaria importanza per recuperare i dati per un modello specifico e i discendenti del modello. La ragione per cui sto usando questo servizio è perché la rotta per questo particolare tipo sta usando una lumaca che non è la chiave primaria e quindi deve fare uno store.query invece di store.find. Quando recuperiamo questo modello, ho una logica che fa capolino nell'archivio di Ember per vedere se possiamo caricarlo da lì prima di andare alla query api. Anche questo venditore sta guardando per il cambiamento di slug e l'aggiornamento del modello corrente basato su quello.Unit Testing Ember Servizi che prelevano dati

Il problema che sto avendo è che questo sembra avere pochissima documentazione quando si tratta di testare una cosa come questa. In effetti non vedo una sezione sui servizi di prova in nessuna delle guide qui http://guides.emberjs.com/v2.1.0/

Questo è uno snippet del servizio in questione.

import Ember from 'ember'; 

export default Ember.Service.extend({ 
    _vendorSlug: null, 
    vendor: null, 

    vendorSlug: function (key, value) { 
     if (arguments.length > 1) { 
      if (this._vendorSlug) { 
       return this._vendorSlug; 
      } 

      this._vendorSlug = value; 
     } 

     return this._vendorSlug; 
    }.property(), 

    ensureVendorLoaded: function (slug) { 
     var service = this, 
      vendorSlug = slug || service.get('vendorSlug'), 
      currentVendor = service.get('vendor'), 
      storedVendor; 

     if (!Ember.isNone(currentVendor) && (vendorSlug === currentVendor.get('slug'))) { 
      return new Ember.RSVP.Promise((resolve) => { 
         resolve(currentVendor); 
        }); 
     } else { 
      var storedVendors = service.store.peekAll('vendor').filter((vendor) => { 
       return vendor.get('slug') === vendorSlug; 
      }); 

      if (storedVendors.length) { 
       storedVendor = storedVendors[0]; 
      } 
     } 

     if (!Ember.isNone(storedVendor)) { 
      service.set('vendorSlug', storedVendor.get('slug')); 

      return new Ember.RSVP.Promise((resolve) => { 
        resolve(storedVendor); 
       }); 
     } 

     return service.store.queryRecord('vendor', {slug: vendorSlug}).then((vendor) => { 
      service.set('vendor', vendor); 
      service.set('vendorSlug', vendor.get('slug')); 

      return vendor; 
     }); 
    }, 

    _vendorSlugChanged: function() { 
     if (this.get("vendorSlug") === this.get("vendor.slug")) { 
      return; 
     } 

     this.ensureVendorLoaded(); 
    }.observes('vendorSlug') 
}); 

Mi piacerebbe poter affermare un paio di scenari qui con l'interazione del negozio. Fornitore già impostato, fornitore caricato dal filtro di sbirciata e venditore caricato dalla query.

+0

Sto anche cercando di fare qualcosa di simile e non riesco a trovare alcuna documentazione sull'argomento. Ho iniziato a guardare alcuni progetti che usano ember e spero di trovare presto alcuni esempi. [Ecco cosa ho finora] (http://pastebin.com/8sMFNdQN) se aiuta a tutti. – cojomojo

+1

@cojomojo come sarebbe il modulo 'MockStore' che hai commentato? – BillPull

+0

vedere la mia risposta per un esempio – cojomojo

risposta

6

Penso di aver finalmente raggiunto una conclusione ragionevole. Permettetemi di condividere con voi quello che penso possa essere il modo migliore per avvicinarsi ai servizi di test unitari che si basano sul negozio.

La risposta sta proprio nell'ipotesi che dobbiamo fare quando scriviamo unit tests. Cioè, tutto ciò che è al di fuori della nostra unità logica dovrebbe essere considerato per funzionare correttamente e le nostre unità dovrebbero essere completamente indipendenti.

Così, con un servizio basandosi sul negozio è meglio per creare uno stub o finto (vedi this question per capire la differenza tra un modello e uno stub) per il negozio. Uno stub per il negozio stesso è abbastanza semplice. Qualcosa di simile lo farà:

store: { 
    find: function() { 
     var mockedModel = Ember.Object.create({/*empty*/}); 
     return mockedModel; 
    }, 
    query: ... 
} 

Se si preferisce utilizzare un mock invece si potrebbe fare qualcosa di simile a quanto segue (Ho fatto questo veramente veloce quindi potrebbe non funzionare del tutto, ma la sua abbastanza per avere l'idea di diametro):

import Ember from 'ember'; 

class MockStore { 
    constructor() { 
     this.models = Ember.A([]); 
    } 

    createRecord(modelName, record) { 
     // add a save method to the record 
     record.save =() => { 
      return new Ember.RSVP.Promise((resolve) => { 
       resolve(true); 
      }); 
     }; 

     if (!this.models[modelName]) { 
      this.models[modelName] = Ember.A([]); 
     } 

     this.models[modelName].pushObject(record); 

     return record; 
    } 

    query(modelName, query) { 
     let self = this; 

     return new Ember.RSVP.Promise((resolve) => { 
      let model = self.models[modelName]; 
      // find the models that match the query 
      let results = model.filter((item) => { 
       let result = true; 

       for (let prop in query) { 
        if (query.hasOwnProperty(prop)) { 
         if (!item[prop]) { 
          result = false; 
         } 
         else if (query[prop] !== item[prop]) { 
          result = false; 
         } 
        } 
       } 

       return result; 
      }); 

      resolve(results); 
     }); 
    } 
} 

export default MockStore; 

Prossimo tutto quello che dovete fare è impostare la proprietà negozio (o qualunque sia il vostro chiamandolo) sul vostro servizio per una nuova istanza negozio finta quando si esegue un test. Ho fatto questo in questo modo:

import Ember from 'ember'; 
import { moduleFor, test } from 'ember-qunit'; 
import MockStore from '../../helpers/mock-store'; 

let session; 
let store; 

const username = ''; 
const password = ''; 

moduleFor('service:authentication', 'Unit | Service | authentication', { 
    beforeEach() { 
     session = Ember.Object.create({}); 
     store = new MockStore(); 
    } 
}); 

test('it should authenticate the user', function (assert) { 
    // this sets the store property of the service to the mock store 
    let authService = this.subject({session: session, store: store}); 

    authService.authenticate(username, password).then(() => { 
     assert.ok(session.get('username')); 
    }); 
}); 

La documentazione sui test queste situazioni è sicuramente povero, quindi forse c'è un metodo migliore, ma questo è ciò che io voglio essere a rotazione con per ora.Inoltre, se si controlla il progetto Discourse, che utilizza la brace, seguono uno schema simile a quello che ho descritto qui, ma in un modo un po 'più avanzato.

+0

Sono sicuro che un PR con documentazione per qualcosa lungo queste linee sarebbe accettato. – knownasilya

+0

@knownasilya, sto lavorando su quello attualmente! – cojomojo

1

Non sono sicuro che questa sia la risposta che vuoi, ma lo farò comunque. Un servizio Ember non è molto più di un oggetto Ember e se si esegue il "test unitario" di quel servizio, dovrebbe essere in isolamento rispetto alle sue dipendenze (altrimenti non sarebbe un unit test).

Dalla mia comprensione (e questo potrebbe essere sbagliato). Se si desidera testare tale servizio è necessario sostituire lo store con un'implementazione fittizia.

//tests/unit/services/my-service.js 
test('some scenario', function(assert) { 
    let service = this.subject({ 
    store: Ember.Object.create({ 
     peekAll(modelName){ 
      //Return array for this scenario 
     }, 
     query(model, params){ 
      //Return array for this scenario 
     } 
    }); 
    }); 
    assert.ok(service); 
}); 

Penso anche che questo sia il motivo per cui ci sono pochi servizi di test di documentazione. Una risorsa che consiglio sui servizi è questa conversazione from the Chicago Ember Meetup

+0

C'è un modo per farlo in un metodo di installazione o qualcosa del genere? – BillPull

+0

Ti indicherò la risposta che @comojo ha lasciato sotto. Rende il negozio un file esterno e lo importa nel test (e crea un'istanza dello store nel primo di tutti) –