2017-02-21 5 views
9

Ich habe einen Dienst (ChildService), der von einem anderen Dienst (InteractWithServerService) abhängt. Späterer Dienst (InteractWithServerService) wird verwendet, um Serveraufrufe auszuführen und eine Observable vom Typ "any" zurückzugeben. Der Einfachheit halber nehmen wir an, dass es beobachtbar zurückgegeben wird. Ich versuche Komponententest für ChildService zu schreiben.Unit-Test spyOn beobachtbarer Service in angular2

ChildService

@Injectable() 
export class ApplicationService { 
    constructor(private interactWithServerService:InteractWithServerService){;} 

    public GetMeData():string { 
     var output:string;  
     this.interactWithServerService.get("api/getSomeData"). 
      subscribe(response =>{console.log("server response:", response); 
      output=response});   
     return output; 
    } 
} 

ServerInteractionService

@Injectable() 
export class InteractWithServerService {   
    constructor(private http: Http) { 
     ; 
    }  
    get(url: string): Observable<any> {   
     return this.http.get(this.url); 
    }  
} 

Der Testfall funktioniert gut, wenn ich den abhängigen Dienst verspotten. d.h.

class MockInteractWithServerService { 
    get() { 
     return Observable.of("some text"); 
    }   
} 

describe('Service:ChildService',() => { 
    let childService: ChildService; 

    beforeEach(() => { 
     TestBed.configureTestingModule({ 
      providers: [ 
      { provide: InteractWithServerService, useClass: MockInteractWithServerService }, 
       ChildService], 
     }); 


    beforeEach(inject([ChildService], (actualService: ChildService) => { 
     childService= actualService;   
    })); 

    fit('should call server-call testCall()',() => { 
     let actualReturnvalue= childService.GetMeData();   
     expect(actualReturnvalue).toBe("some text"); 
    }); 
}); 

Das oben beschriebene Verfahren ist nicht die bevorzugt als I „n“ mock Klassen für „n“ Abhängigkeiten vielleicht am Ende zu schreiben. Also möchte ich meine Unit Tests mit SpyOn erstellen. Der Testfall funktioniert jedoch nicht und wirft "Fehler: Kein Provider für Http!". Während ich verstehe, was der Fehler ist, möchte ich wissen, warum es geworfen wird, obwohl ich den abhängigen Dienst ausspioniere. Sieht so aus, als ob "SpyOn" nicht funktioniert.

describe('Service:ChildService',() => { 
    let childService: ChildService; 

    beforeEach(() => { 
     TestBed.configureTestingModule({ 
      providers: [ 
      InteractWithServerService, 
       ChildService], 
     }); 

     spyOn(InteractWithServerService.prototype, 'get').and 
      .callFake(()=>  
      {return Observable.of("some text");});  
    }); 
    beforeEach(inject([ChildService], (actualService: ChildService) => { 
     childService= actualService;   
    })); 

    fit('should call server-call testCall()',() => { 
     let actualReturnvalue= childService.GetMeData();   
     expect(actualReturnvalue).toBe("some text"); 
    }); 
}); 

Fehle ich etwas offensichtlich?

Antwort

8

However, the test case doesn't work and throws "Error: No provider for Http!".

Weil du immer noch den Dienst in der providers haben, so Angular versucht, es kann immer noch

providers: [ 
InteractWithServerService, 
    ChildService], 

Was Sie ist statt tun, um zu erstellen, um nur etwas tun, ein Mock Klasse erstellen, wie

providers: [ 
    { 
    provide: InteractWithServerService, 
    useValue: { get: Observable.of(..) } 
    } 
] 

Hier ist man useValue verwenden, die jedes Objekt zur Verfügung stellen. Das ist der Wert, der beim Einspritzen verwendet wird. Im obigen Fall ist es nur ein beliebiges Objekt mit Ihrer Scheinmethode.

Wenn Sie so wollen spy, dass Sie verschiedene Werte zur Verfügung stellen kann, können Sie die InteractWithServerService injizieren konnte, und dann tun

spyOn(service, 'get').and.returnValue(Observable.of(...)) 
// do test 

Eine andere Sache, die Sie tun können, ist mock das Http mit einem Dummy-Objekt

{ provide: Http, useValue: {} } 

Jetzt funktioniert die InteractWithServerService (Hinzufügen der Klasse zu den Providern, wie Sie derzeit haben). Und Sie können es einfach ausspionieren

let service = TestBed.get(InteractWithServerService); 
spyOn(service, 'get').and.returnValue(..) 
// do test 
+0

Dank @peeskillet. Klappt wunderbar. Es geht nur um "useValue" und die Rückgabe eines beliebigen Objekts für jede Methode, die Sie aufrufen. Es sieht definitiv dreckig aus (eher nicht so sauber). Ist das die Art und Weise, wie wir die Tests schreiben sollen? –

+0

Ein "beliebiges Objekt" unterscheidet sich nicht wirklich von der Verwendung einer Scheinklasse. Einziger Unterschied ist, dass man in einer Klasse gekapselt ist. Am Ende sind sie beide nur willkürliche Objekte. Ihre Klasse erweitert die echte Service-Schnittstelle nicht. Was macht das nicht willkürlich? Es kommt nur darauf an, dass das Objekt die Methode hat, die aufgerufen wird. –

+0

Nein, ich argumentiere, dass Scheinklassen besser sind als UseValue.Mein Punkt ist beides - Scheinklasse und UseValue-Lösung sieht im Vergleich zu alten Methoden nicht sauber aus, sondern injiziert nur abhängige Klassen und spioniert sie aus. –