source

Typescript-Interface/Type-definition을 스터브하는 방법

lovecheck 2023. 2. 17. 21:37
반응형

Typescript-Interface/Type-definition을 스터브하는 방법

저는 AngularJS 1.X 프로젝트에서 Typescript와 함께 일하고 있습니다.나는 다른 목적으로 다른 Javascript 라이브러리를 사용한다.소스를 유닛 테스트하기 위해 타이핑(= 인터페이스)을 사용하여 종속성을 스터브하려고 합니다.ANY 타입을 사용하지 않고 각 인터페이스 메서드에 빈 메서드를 쓰는 것도 원하지 않습니다.

저는 그런 방법을 찾고 있습니다.

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

하고 있는 은, 는 가가지 the the, 가 use use use use use use use use use 를 사용하고 있는 것입니다.any테스트 케이스에서 호출된 모든 메서드를 구현하거나 인터페이스를 구현하고 전체 인터페이스를 구현합니다.[ ] : ] : 。

각 메서드에 대해 구현이 비어 있고 입력된 개체를 생성하려면 어떻게 해야 합니까?나는 시논을 조롱의 목적으로 사용하고 있지만, 다른 도서관도 이용할 수 있다.

PS: Typescript에 의해 인터페이스가 지워지는 것은 알고 있습니다.그래도 해결하고 싶다:)

저는 qUnit과 Sinon을 사용하여 Typescript 테스트를 작성했고, 당신이 설명한 것과 똑같은 고통을 경험했습니다.

다음과 같은 인터페이스에 의존하고 있다고 가정합니다.

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

저는 sinon stub/spy와 주조물에 기반한 몇 가지 방법을 사용하여 추가 공구/라이브러리의 필요성을 가까스로 피했습니다.

  • 빈 오브젝트 리터럴을 사용하여 코드에서 사용되는 함수에 sinon stub을 직접 할당합니다.

    //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);
    
  • 코드에 필요한 메서드의 빈 구현과 함께 오브젝트 리터럴을 사용한 후 필요에 따라 메서드를 sinon spy/stub로 랩합니다.

    //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);
    

qUnit 모듈에서 제공하는 것과 같은 sinon 샌드박스 및 공통 셋업/해체와 결합하면 매우 잘 작동합니다.

  • 공통 설정에서 종속성에 대한 새 샌드박스와 모의 개체 리터럴을 생성합니다.
  • 테스트에서는 spy/stub만 지정하면 됩니다.

다음과 같은 경우(첫 번째 옵션을 사용하지만 두 번째 옵션을 사용하는 경우에도 동일하게 작동합니다).

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");
});

아직 이상적인 솔루션은 아니지만 상당히 잘 작동하고 추가 라이브러리가 필요하지 않으며 필요한 추가 코드의 양을 관리 가능한 수준으로 유지합니다.

최신 TypeMoq(버전 1.0.2)는 런타임(nodejs/browser)이 ES6에 의해 도입된 Proxy 글로벌개체를 지원하는 한 TypeScript 인터페이스를 조롱할 수 있습니다.

'아예'라고 IDependency음음음같 뭇매하다

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

TypeMoq로 조롱하는 것은 다음과 같습니다.

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");

간략한 답변은 Typescript에서는 컴파일 시간이나 런타임 "반영"을 제공하지 않기 때문에 가능하지 않다는 것입니다.모의 라이브러리는 인터페이스의 구성원을 반복할 수 없습니다.

스레드 참조:https://github.com/Microsoft/TypeScript/issues/1549

이는 의존관계를 조롱하는 것이 개발 워크플로우의 중심인 TDD 개발자들에게는 불행한 일입니다.

단, 다른 답변에서 설명한 바와 같이 메서드를 신속하게 스텁하기 위한 많은 기술이 있습니다.이 옵션들은 정신적인 적응을 조금 더 하면 효과가 있을 것이다.

편집: Typescript Abstract Syntax Tree(AST)는 컴파일 시간 "내부 검사"로, 아마도 mock을 생성하는 데 사용될 수 있습니다.하지만 실용적인 도서관을 만든 사람이 있는지 모르겠다.

송신원: npmjs:

Mocking interfaces
You can mock interfaces too, just instead of passing type to mock function, set mock function generic type Mocking interfaces requires Proxy implementation

let mockedFoo:Foo = mock<FooInterface>(); // instead of mock(FooInterface)
const foo: SampleGeneric<FooInterface> = instance(mockedFoo);

ts-blockito는 버전 2.4.0 이후의 모의 인터페이스를 지원합니다.

할 수 은 거의 .TypeMoq,TeddyMocks ★★★★★★★★★★★★★★★★★」Typescript-mockify'아예'는 '아예 '아예'는 '아예'로 되어 있습니다.

github 저장소를 확인하고 원하는 저장소를 선택합니다.링크:

좀 더 도 있지만 시논 리브를 .<any><IDependency>type (타입스크립트와 함께 Sinon을 사용하는 방법)

moq.ts를 시도할 수 있지만 프록시 개체에 따라 다릅니다.

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

Safe Mock은 꽤 좋지만, 안타깝게도 지금은 유지보수가 되지 않은 것 같습니다.완전히 공개하자면, 나는 작가와 함께 일했었다.

import SafeMock, {verify} from "safe-mock";

const mock = SafeMock.build<SomeService>();

// specify return values only when mocks are called with certain arguments like this
when(mock.someMethod(123, "some arg")).return("expectedReturn");

// specify thrown exceptions only when mocks are called with certain arguments like this
when(mock.someMethod(123, "some arg")).throw(new Error("BRR! Its cold!")); 

// specify that the mock returns rejected promises with a rejected value with reject
when(mock.someMethod(123)).reject(new Error("BRR! Its cold!"));

//use verify.calledWith to check the exact arguments to a mocked method
verify(mock.someMethod).calledWith(123, "someArg");

SafeMock은 모크에서 잘못된 타입을 반환할 수 없습니다.

interface SomeService {
    createSomething(): string;
}

const mock: Mock<SomeService> = SafeMock.build<SomeService>();

//Won't compile createSomething returns a string
when(mock.createSomething()).return(123); 

이제 가능합니다.인터페이스 메타데이터를 런타임에 사용할 수 있도록 하는 향상된 버전의 타이프스크립트 컴파일러를 출시했습니다.예를 들어 다음과 같이 쓸 수 있습니다.

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!!

출력은 다음과 같습니다.

$ 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
        -----

이 모든 정보를 사용하여 필요에 따라 프로그래밍 방식으로 스터브를 구축할 수 있습니다.

프로젝트는 여기서 보실 수 있습니다.

언급URL : https://stackoverflow.com/questions/37027776/how-to-stub-a-typescript-interface-type-definition

반응형