Cómo simular (mock) objetos de interoperabilidad con JavaScript
Aprende cómo simular (mock) objetos de JS interop en Dart para pruebas.
En este tutorial, aprenderás cómo simular (mock) objetos de JS para que puedas probar los miembros de instancia de interoperabilidad sin tener que usar una implementación real.
Antecedentes y motivación
#La simulación (mocking) de clases en Dart se realiza normalmente mediante la sobrescritura de miembros de instancia. Sin embargo, dado que se utilizan extension types para declarar tipos de interoperabilidad, todos los miembros de un extension type se despachan estáticamente y, por lo tanto, no se puede utilizar la sobrescritura. Esta limitación también se aplica a los miembros de extensión (extension members), y por lo tanto, no se pueden simular los miembros de instancia de extension types o de extensiones.
Aunque esto se aplica a cualquier miembro de un extension type que no sea external, los miembros
de interoperabilidad external son especiales, ya que invocan miembros en un valor de JS.
extension type Date(JSObject _) implements JSObject {
external int getDay();
}
Como se discutió en la sección de Uso, llamar a getDay()
dará como resultado la llamada a
getDay() en el objeto de JS. Por lo tanto, al usar un JSObject diferente, se puede
llamar a una implementación diferente de getDay.
Para hacer esto, debe haber algún mecanismo para crear un objeto de JS que
tenga una propiedad getDay que, al ser llamada, llame a una función de Dart. Una forma sencilla
es crear un objeto de JS y establecer la propiedad getDay en un callback convertido,
por ejemplo:
final date = Date(JSObject());
date['getDay'] = (() => 0).toJS;
Aunque esto funciona, es propenso a errores y no escala bien cuando estás
utilizando muchos miembros de interoperabilidad. Tampoco maneja los getters o setters de forma adecuada.
En su lugar, deberías usar una combinación de createJSInteropWrapper
y
@JSExport
para declarar un tipo que proporcione una implementación para todos los
miembros de instancia external.
Ejemplo de simulación (mocking)
#import 'dart:js_interop';
import 'package:expect/minitest.dart';
// The Dart class must have `@JSExport` on it or at least one of its instance
// members.
@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();
// Returns a JS object whose properties call the relevant instance members in
// `fakeCounter`.
var counter = createJSInteropWrapper<FakeCounter>(fakeCounter) as Counter;
// Calls `FakeCounter.value`.
expect(counter.value, 0);
// `FakeCounter.renamedIncrement` is renamed to `increment`, so it gets
// called.
counter.increment();
expect(counter.value, 1);
expect(fakeCounter.value, 1);
// Changes in the fake affect the wrapper and vice-versa.
fakeCounter.value = 0;
expect(counter.value, 0);
counter.decrement();
// Because `Counter.decrement` is non-`external`, we never called
// `FakeCounter.decrement`.
expect(counter.value, -2);
}
@JSExport te permite declarar una clase que se puede usar en
createJSInteropWrapper. createJSInteropWrapper creará un objeto literal
que mapea cada uno de los nombres de los miembros de instancia de la clase (o sus nombres renombrados) a un callback de JS,
el cual se crea usando Function.toJS. Cuando se le llama, el callback de JS
a su vez llamará al miembro de instancia. En el ejemplo anterior, obtener y establecer
counter.value obtiene y establece fakeCounter.value.
Puedes especificar que solo se exporten algunos miembros de una clase omitiendo la
anotación de la clase y en su lugar anotar únicamente los miembros específicos. Puedes
ver más detalles sobre exportaciones más especializadas (incluyendo la herencia) en
la documentación de @JSExport.
Ten en cuenta que este mecanismo no es específico para pruebas únicamente. Puedes usar esto para proporcionar una interfaz de JS para un objeto de Dart arbitrario, lo que te permite esencialmente exportar objetos de Dart a JS con una interfaz predefinida.
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2025-09-15. Ver fuente oreportar un problema.