如何模拟 JavaScript 互操作对象
本教程将教你如何模拟 JS 对象,以便在无需使用真实实现的情况下测试互操作实例成员。
背景和动机
#在 Dart 中模拟类通常通过重写实例成员来完成。 但是,由于使用扩展类型来声明互操作类型,所有扩展类型成员都是静态分发的,因此无法使用重写。这个限制也适用于扩展成员,因此无法模拟实例扩展类型或扩展成员。
虽然这适用于任何非 external
扩展类型成员,但 external
互操作成员是特殊的,因为它们调用 JS 值上的成员。
extension type Date(JSObject _) implements JSObject {
external int getDay();
}
如用法部分所述,调用 getDay()
将导致调用 JS 对象上的 getDay()
。因此,通过使用不同的 JSObject
,可以调用 getDay
的不同 实现 。
为此,应该有一些机制来创建具有 getDay
属性的 JS 对象,当调用该属性时,会调用 Dart 函数。一种简单的方法是创建一个 JS 对象,并将 getDay
属性设置为转换后的回调,例如:
final date = Date(JSObject());
date['getDay'] = (() => 0).toJS;
虽然此方法有效,但它容易出错,并且在使用许多互操作成员时难以扩展。它也没有正确处理 getter 或 setter。相反,您应该结合使用createJSInteropWrapper
和 @JSExport
来声明一个为所有 external
实例成员提供实现的类型。
模拟示例
#import 'dart:js_interop';
import 'package:expect/minitest.dart';
// Dart 类必须在其上或至少在其一个实例成员上具有 `@JSExport` 。
@JSExport()
class FakeCounter {
int value = 0;
@JSExport('increment')
void renamedIncrement() {
value++;
}
void decrement() {
value--;
}
}
extension type Counter(JSObject _) implements JSObject {
external int value;
external void increment();
void decrement() {
value -= 2;
}
}
void main() {
var fakeCounter = FakeCounter();
// 返回一个 JS 对象,其属性调用 `fakeCounter` 中的相关实例成员。
var counter = createJSInteropWrapper<FakeCounter>(fakeCounter) as Counter;
// 调用 `FakeCounter.value` 。
expect(counter.value, 0);
// `FakeCounter.renamedIncrement` 重命名为 `increment` ,因此它被调用。
counter.increment();
expect(counter.value, 1);
expect(fakeCounter.value, 1);
// 模拟对象的更改会影响包装器,反之亦然。
fakeCounter.value = 0;
expect(counter.value, 0);
counter.decrement();
// 因为 `Counter.decrement` 不是 `external` ,所以我们从未调用 `FakeCounter.decrement` 。
expect(counter.value, -2);
}
@JSExport
允许您声明一个可在 createJSInteropWrapper
中使用的类。 createJSInteropWrapper
将创建一个对象字面量,该字面量将类的每个实例成员名称(或重命名)映射到一个 JS 回调,该回调使用 Function.toJS
创建。当被调用时,JS 回调将依次调用实例成员。在上面的示例中,获取和设置 counter.value
将获取和设置 fakeCounter.value
。
您可以通过从类中省略注释并仅注释特定成员来指定要导出的类的某些成员。您可以在@JSExport
的文档中看到有关更专业导出(包括继承)的更多详细信息。
请注意,此机制并非仅限于测试。您可以使用它为任意 Dart 对象提供 JS 接口,从而允许您 essentially 导出 Dart 对象到 JS,并使用预定义的接口。
除非另有说明,否则本网站上的文档反映的是 Dart 3.6.0。页面最后更新于 2025-02-05。 查看源代码 或 报告问题.