2016-05-04 33 views
10

Ich arbeite mit Typescript in einem AngularJS 1.X Projekt. Ich benutze verschiedene Javascript-Bibliotheken für verschiedene Zwecke. Zum Testen meiner Quelle möchte ich einige Abhängigkeiten mit den Typings (= Interfaces) stubben. Ich möchte den ANY-Typ nicht verwenden und auch keine leere Methode für jede Schnittstellenmethode schreiben.Wie stemple ich eine Typoskript-Schnittstelle/Typ-Definition?

Im Suche nach einem Weg, so etwas zu tun:

let dependency = stub(IDependency); 
stub(dependency.b(),() => {console.log("Hello World")}); 
dependency.a(); // --> Compile, do nothing, no exception 
dependency.b(); // --> Compile, print "Hello World", no exception 

Der Schmerz, den ich jetzt habe, ist, dass ich entweder verwenden any und alle Methoden implementieren, die in meinem Testfall aufgerufen oder ich implementieren die Schnittstelle und implementieren Sie die vollständige Schnittstelle. Das ist zu viel unnützer Code :(.

Wie kann ich ein Objekt erzeugen, die eine leere Implementierung für jede Methode hat und eingegeben wird? Ich benutze Sinon für Zwecke spöttisch, aber im offenen anderen Bibliotheken zu verwenden.

PS: Ich weiß, dass Typescript die Schnittstellen löscht ... aber ich möchte das immer noch lösen :).

Antwort

4

Ich denke, die kurze Antwort ist, dass diese ist nicht möglich in Maschinenschrift, wie die Sprache keine Übersetzungszeit bietet oder Laufzeit „Reflexion“. Es ist nicht möglich, dass eine Scheinbibliothek die Mitglieder einer Schnittstelle iteriert.

See thread: https://github.com/Microsoft/TypeScript/issues/1549

Dies ist bedauerlich für TDD-Entwickler, in denen eine Abhängigkeit spöttischen ist ein zentraler Bestandteil der Entwicklung Workflow.

Es gibt jedoch eine Reihe von Techniken, um die Methoden schnell zu stubben, wie in den anderen Antworten beschrieben. Diese Optionen könnten die Arbeit mit ein wenig mentaler Anpassung erledigen.

2

Es gibt nur wenige Bibliotheken, die dies ermöglichen TypeMoq, TeddyMocks und Typescript-mockify sind wahrscheinlich eine der beliebtesten.

die GitHub Repositories prüfen und auf einen Blick Sie besser gefällt: Links:

Sie auch verwenden können mehr populäre libs wie Sinon, aber zuerst müssen Sie einen <any> Typ und dann Narro verwenden w it to <IDependency> type (How do I use Sinon with Typescript?)

+0

Sie alle benötigen eine Klasse, um einen Schein zu erstellen, eine Schnittstelle ist nicht genug. Ich denke, die Art der Löschung macht es unmöglich, ohne Typescript selbst zu hacken -> http://stackoverflow.com/questions/13142635/how-can-i-create-an-object-based-on-an-interface-file- definition-in-typescript – user1879408

+0

Was ist mit dem Erstellen eines leeren Objekts, das Ihre Schnittstelle implementiert? und übergib es als Gegenstand an deinen Spott? – PolishDeveloper

+0

Das schafft die Methoden nicht -> Type löschen;) – user1879408

12

Ich habe Typoskript Tests geschrieben mit qUnit und Sinon, und ich habe genau die gleichen Schmerzen erlebt, die Sie beschreiben.

Nehmen wir an, Sie eine Abhängigkeit von einer Schnittstelle wie haben: basierend auf sinon Stubs/Spione und Gießen

interface IDependency { 
    a(): void; 
    b(): boolean; 
} 

Ich habe es geschafft, indem Sie ein paar Ansätze, um die Notwendigkeit von zusätzlichen Tools/Bibliotheken zu vermeiden.

  • Verwenden eines leeren Objektliteral, dann direkt zuweisen Sinon Stubs zu den Funktionen im Code verwendet:

    //Create empty literal as your IDependency (usually in the common "setup" method of the test file) 
    let anotherDependencyStub = <IDependency>{}; 
    
    //Set stubs for every method used in your code 
    anotherDependencyStub.a = sandbox.stub(); //If not used, you won't need to define it here 
    anotherDependencyStub.b = sandbox.stub().returns(true); //Specific behavior for the test 
    
    //Exercise code and verify expectations 
    dependencyStub.a(); 
    ok(anotherDependencyStub.b()); 
    sinon.assert.calledOnce(<SinonStub>anotherDependencyStub.b); 
    
  • Verwenden Objektliteral mit leeren Implementierungen der Methoden, die von der Code benötigt wird, dann wickeln Methoden in sinon Spione/Stubs als

    //Create dummy interface implementation with only the methods used in your code (usually in the common "setup" method of the test file) 
    let dependencyStub = <IDependency>{ 
        a:() => { }, //If not used, you won't need to define it here 
        b:() => { return false; } 
    }; 
    
    //Set spies/stubs 
    let bStub = sandbox.stub(dependencyStub, "b").returns(true); 
    
    //Exercise code and verify expectations 
    dependencyStub.a(); 
    ok(dependencyStub.b()); 
    sinon.assert.calledOnce(bStub); 
    

erforderlich arbeiten Sie ganz schön, wenn man Kombiniere sie mit Sinon-Sandboxen und dem üblichen Setup/Teardown, wie es von qUnit-Modulen bereitgestellt wird.

  • Im allgemeinen Setup erstellen Sie eine neue Sandbox und die Mock-Objektliterale für Ihre Abhängigkeiten.
  • Im Test geben Sie nur die Spione/Stubs an.

So etwas wie diese (die erste Option verwenden, würde aber die gleiche Art und Weise funktionieren, wenn Sie die zweite Option bewertet wurden):

QUnit["module"]("fooModule", { 
    setup:() => { 
     sandbox = sinon.sandbox.create(); 
     dependencyMock = <IDependency>{}; 
    }, 
    teardown:() => { 
     sandbox.restore(); 
    } 
}); 

test("My foo test",() => { 
    dependencyMock.b = sandbox.stub().returns(true); 

    var myCodeUnderTest = new Bar(dependencyMock); 
    var result = myCodeUnderTest.doSomething(); 

    equal(result, 42, "Bar.doSomething returns 42 when IDependency.b returns true"); 
}); 

Ich würde zustimmen, dass diese noch nicht die ideale Lösung, aber es funktioniert recht gut, erfordert keine zusätzlichen Bibliotheken und hält den benötigten zusätzlichen Code auf einem niedrigen Niveau.

1

Jetzt ist es möglich. Ich habe eine erweiterte Version des Typskript-Compilers veröffentlicht, der zur Laufzeit Schnittstellen-Metadaten zur Verfügung stellt. Zum Beispiel können Sie schreiben:

interface Something { 

} 

interface SomethingElse { 
    id: number; 
} 

interface MyService { 
    simpleMethod(): void; 
    doSomething(p1: number): string; 
    doSomethingElse<T extends SomethingElse>(p1: Something): T; 
} 

function printMethods(interf: Interface) { 
    let fields = interf.members.filter(m => m.type.kind === 'function'); //exclude methods. 
    for(let field of fields) { 
     let method = <FunctionType>field.type; 
     console.log(`Method name: ${method.name}`); 
     for(let signature of method.signatures) { 
      //you can go really deeper here, see the api: reflection.d.ts 
      console.log(`\tSignature parameters: ${signature.parameters.length} - return type kind: ${signature.returns.kind}`); 
      if(signature.typeParameters) { 
       for(let typeParam of signature.typeParameters) { 
        console.log(`\tSignature type param: ${typeParam.name}`); //you can get constraints with typeParam.constraints 
       } 
      } 
      console.log('\t-----') 
     } 
    } 
} 

printMethods(MyService); //now can be used as a literal!! 

und dies ist die Ausgabe:

$ node main.js 
Method name: simpleMethod 
     Signature parameters: 0 - return type kind: void 
     ----- 
Method name: doSomething 
     Signature parameters: 1 - return type kind: string 
     ----- 
Method name: doSomethingElse 
     Signature parameters: 1 - return type kind: parameter 
     Signature type param: T 
     ----- 

Mit all diesen Informationen können Sie Stubs programmatisch bauen, wie Sie bevorzugen.

Sie können mein Projekt here finden.

8

Neueste TypeMoq (ver 1.0.2) unterstützt Typoskript Schnittstellen spöttisch, solange die Laufzeit (NodeJS/Browser) unterstützt die Proxy globale Objekt von ES6 eingeführt.

So sieht IDependency vorausgesetzt wie folgt aus:

interface IDependency { 
    a(): number; 
    b(): string; 
} 

dann mit TypeMoq verspotten würde, da dies so einfach sein:

import * as TypeMoq from "typemoq"; 
... 
let mock = TypeMoq.Mock.ofType<IDependency>(); 

mock.setup(x => x.b()).returns(() => "Hello World"); 

expect(mock.object.a()).to.eq(undefined); 
expect(mock.object.b()).to.eq("Hello World"); 
0

Sie können moq.ts versuchen, aber es hängt von Objekt Proxy

interface IDependency { 
    a(): number; 
    b(): string; 
} 


import {Mock, It, Times} from 'moq.ts'; 

const mock = new Mock<IDependency>() 
    .setup(instance => instance.a()) 
    .returns(1); 

mock.object().a(); //returns 1 

mock.verify(instance => instance.a());//pass 
mock.verify(instance => instance.b());//fail