当前位置: 首页 > 知识库问答 >
问题:

如何存根打字稿接口/类型定义?

奚才良
2023-03-14

我在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

我现在的痛苦是,我要么使用< code>any并实现我的测试用例中调用的所有方法,要么实现接口并实现完整的接口。那太多没用的代码了:(。

如何生成每个方法都有空实现并被键入的对象?我使用Sinon进行模拟,但我也可以使用其他库。

PS:我知道打字稿会擦除接口……但我仍然想解决这个问题:)。

共有3个答案

纪辰沛
2023-03-14

我认为简单的答案是,这在Typescript中是不可能的,因为该语言不提供编译时或运行时“反射”。模拟库不可能迭代接口的成员。

参见线程:https://github.com/Microsoft/TypeScript/issues/1549

这对TDD开发人员来说是不幸的,因为模仿依赖是开发工作流程的核心部分。

然而,正如其他答案所描述的,有许多快速存根方法的技术。这些选项可能会完成这项工作,只要稍加心理调整。

编辑:打字稿抽象语法树AST是一个编译时的“自省”——它可能用于生成模拟。但是,我不知道是否有人制作了一个实用的库。

充栋
2023-03-14

最新的TypeMoq(ver 1.0.2)支持模拟TypeScript接口,只要运行时(nodejs/browser)支持ES6引入的Proxy全局对象。

因此,假设IDependence如下所示:

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");
奚翰海
2023-03-14

我一直在使用qUnit和Sinon编写打字稿测试,我经历了与您描述的完全相同的痛苦。

假设您依赖于以下接口:

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

通过使用几种基于 sinon 存根/间谍和转换的方法,我设法避免了对其他工具/库的需求。

>

  • 使用空对象文字,然后直接将sinon存根分配给代码中使用的函数:

    //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间谍/存根中

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

    当您将它们与Sinon沙盒和常见的设置/拆卸(如qUnit模块提供的设置/拆卸)结合使用时,它们的工作效果非常好。

    • 在通用设置中,您为您的依赖项创建一个新的沙盒和模拟对象文字。
    • 在测试中,您只需指定间谍/存根。

    类似这样的情况(使用第一个选项,但如果使用第二个选项,则工作方式相同):

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

    我同意这仍然不是理想的解决方案,但它工作得相当好,不需要额外的库,并将所需的额外代码量保持在可管理的低水平。

  •  类似资料:
    • 我对打字稿有点陌生,让我有点困惑的一件事是类型/接口声明。我正在编写一个库,它目前有几个类和更多的类型/接口声明。我在大多数代码中使用这些类型/接口,最初我会将它们放在 types 文件夹中的单个文件中,然后我会将它们导入到需要的任何位置,现在我在 文件夹中有一个声明命名空间类型的 ,在这个中我导出我在整个代码中使用的所有类型/接口。这种方法可以吗,或者是否有更好的方法来组织类型和接口定义?

    • 问题内容: React事件的正确类型是什么。最初,我只是为了简单起见而使用。现在,我正在尝试清理并避免完全使用。 所以像这样一个简单的形式: 我在这里使用什么类型作为事件类型? 似乎没有工作,因为我收到一个错误消息,并且不存在。 对于所有事件的更笼统的回答,我们将不胜感激。 谢谢 问题答案: 该SyntheticEvent接口是通用的: 并且是一般约束与的交集 。 另外,由于事件是由输入元素引起的

    • 我正在处理打字稿,并将一个函数传递给另一个函数。 如果我有一个传递到typescript中另一个函数的函数,我应该如何编写类型? 我尝试了,但似乎不起作用。

    • 问题内容: 我正在获取一个json响应并将其存储在mongodb中,但是我不需要的字段也正在进入数据库中,是否仍要剥离unseccary字段? 输出: 问题答案: 您可以使用从给定对象中选取某些属性的函数: 然后:

    • 我有以下代码: 我遇到一个错误,指向.find并说: “字符串”类型的变量中不存在属性find。我知道错误在说什么,但是我如何将< code>$modal定义为一个我可以使用< code >的变量。查找于? 同样,现在我在typescript世界里,有没有更好的方法来定义modal,而不是在这里将它定义为var?

    • 我的代码: 错误消息来自TypeScript(3.0)编译器: TS2322:类型“string[]”不可分配给类型“[string, string, string, string, string, string, string]”。“string[]”类型中缺少属性“0”。 如果我将字符串[]更改为只读数组 TS2322:“ReadonlyArray”类型不能分配给类型“[string,stri