【问题标题】:Dart How to mock a procedureDart 如何模拟一个过程
【发布时间】:2014-05-29 09:06:49
【问题描述】:

如何模拟一个过程(与函数相对,请参阅here

例如,给定以下 typedef 和过程,

typedef int Adder(int a, int b);

int useAdder(Adder adder) {
  return adder(1, 2);
}

您如何编写一个模拟来测试 userAdder 过程是否调用了您的模拟函数?

这是我的尝试,但失败并显示 test failed: Caught The null object does not have a method 'call'。

class MyMock extends Mock {
  MyMock(){
    when(callsTo('call')).alwaysCall(this.foo);
  }
  int foo(int a, int b) => a+b;
}

void main() {

  test("bb", () {
    var mockf = new MyMock();
    expect(useAdder( mockf.call), 3);
    mockf.getLogs(callsTo('call', 1, 2)).verify(happenedOnce);
  });
}

如果我改变了

 expect(useAdder( mockf.call), 3);

 expect(useAdder( mockf.foo), 3);

方法调用没有出现在日志中

【问题讨论】:

  • 您应该将此问题重命名为“Dart Mocking a Function”,将之前的问题重命名为“Dart Mocking a call method”,以便对 Dart 社区更有帮助。

标签: dart dart-unittest


【解决方案1】:

我的尝试

import 'package:unittest/unittest.dart';
import 'package:mock/mock.dart';

typedef int Adder(int a, int b);

int useAdder(Adder adder) {
  return adder(1, 2);
}

class MyMock extends Mock {
  MyMock(){
    when(callsTo('call')).alwaysCall(this.foo);
  }
  int foo(int a, int b) => a+b;

  int call(int a, int b) => super.call(a, b);

}

void main() {

  test("bb", () {
    var mockf = new MyMock();
    expect(useAdder(mockf as Adder), 3);
    mockf.getLogs(callsTo('call', 1, 2)).verify(happenedOnce);
  });
}

似乎调用方法必须实际存在才能使 MyMock 被接受为 Adder。

【讨论】:

  • 这当然有效,但它和罪恶一样丑陋!您必须定义两个方法,一个完成工作,另一个调用 when() 然后定义何时获取日志记录!所有这一切都是为了一种高度面向程序的语言。我希望 mock 包的作者能想出更简单的东西!!!
  • 我也不喜欢。我刚刚发现code.google.com/p/dart/issues/detail?id=18341 似乎有计划改进它。另见链接问题code.google.com/p/dart/issues/detail?id=18334
  • @richard 是的,它很丑。而且它变得更糟,因为 Dart 不支持可变数量的参数。
  • 你为什么使用@proxy注解?没有它,我看不到任何错误或警告。
  • 这可能是多余的,没有多想。据我记得不久前有必要避免警告。
【解决方案2】:

我使用 Günter Zöchbauer 解决方案的核心思想更新了我的代码。

library functionMockTest;

import "package:unittest/unittest.dart";
import "package:mock/mock.dart";
import "dart:math" as math;


typedef int BinaryIntToInt(int a, int b);
typedef double BinaryIntToDouble(int a, int b);
typedef int DoubleIntToInt(double a, int b);


int useBinaryIntToInt(BinaryIntToInt adder) => adder(1, 2);


void main() {
  test('Mock [min] function from the "dart:math"', () {
    var min = new FunctionMock(math.min);
    expect(min(2,1), 1);
    expect(min(1,2), 1);
    min.calls('call', 1, 2).verify(happenedOnce);
  });

  test("Function Mock", () {
    var adder2 = new FunctionMock<BinaryIntToInt>((a,b) => a + b);
    var adder3 = new FunctionMock((a,b,c) => a + b + c);
    expect(adder2 is Function, true);
    expect(useBinaryIntToInt(adder2), 3);
    expect(adder3(1,2,3), 6);
    adder2.calls('call', 1, 2).verify(happenedOnce);
  });
  group("Type check on function mocks:", (){
    test("Should throw [TypeError] \n "
        "if function has wrong number of arguments", () {
      TypeError error;
      try {
        var adder3 = new FunctionMock<BinaryIntToInt>((a,b,c) => a + b + c);
        }
      catch(exception) {
        expect(exception is TypeError, true);
        error = exception;
        }
      expect(error != null,  true);
    });
    test("Should throw [TypeError] \n "
        "if function has wrong type of arguments", () {
      TypeError error;
      try {
        var adder3 = new FunctionMock<BinaryIntToInt>((double a,b) => 10);
        }
      catch(exception) {
        expect(exception is TypeError, true);
        error = exception;
        }
      expect(error != null,  true);
    });
    test("Doesn't throw on typedef mismatch \n"
        "without type annotation", () {
      BinaryIntToDouble foo = (c,d) => c / d;
      DoubleIntToInt  bar = (c,d) => c + d;
       var wrongTypedefReturnType = new FunctionMock<BinaryIntToInt>(foo);
       var wrongTypedefArgumentType = new FunctionMock<BinaryIntToInt>(bar);
       wrongTypedefReturnType(1.1,2.1);
       wrongTypedefArgumentType(1.1,2.1);

    });
    test("Throws with type annotation", () {
      double foo_ (int c, int d)  => c / d;
      BinaryIntToDouble foo = foo_;
      int  bar_ (double c, int d)  => 10;
      DoubleIntToInt  bar = bar_;
      TypeError returnTypeError, argumentTypeError;
      try {
       var wrongTypedefReturnType = new FunctionMock<BinaryIntToInt>(foo);
      }
      catch(exception) {
        expect(exception is TypeError, true);
        returnTypeError = exception;
        }
      try {
       var wrongTypedefArgumentType = new FunctionMock<BinaryIntToInt>(bar);
      }
       catch(exception) {
         expect(exception is TypeError, true);
         argumentTypeError = exception;
         }
      expect(returnTypeError != null,  true);
      expect(argumentTypeError != null,  true);
    });
  }
  );
}

class _Sentinel {
  const _Sentinel();
}
const NO_ARG = const _Sentinel();

class FunctionMock<FunctionTypedef> extends Mock implements Function{

  final  FunctionTypedef _callable;


  FunctionMock._internal(FunctionTypedef function) : _callable = function {
    when(callsTo('call')).alwaysCall(_callable);
   }
  //Place to 'shovel in' black magic if needed.
  factory  FunctionMock(FunctionTypedef function){
    return new FunctionMock._internal(function);
  }

  call([arg0 = NO_ARG,
        arg1 = NO_ARG,
        arg2 = NO_ARG,
        arg3 = NO_ARG,
        arg4 = NO_ARG,
        arg5 = NO_ARG,
        arg6 = NO_ARG,
        arg7 = NO_ARG,
        arg8 = NO_ARG,
        arg9 = NO_ARG]) {
    if (identical(arg0, NO_ARG)) return super.call();
    if (identical(arg1, NO_ARG)) return super.call(arg0);
    if (identical(arg2, NO_ARG)) return super.call(arg0, arg1);
    if (identical(arg3, NO_ARG)) return super.call(arg0, arg1, arg2);
    if (identical(arg4, NO_ARG)) return super.call(arg0, arg1, arg2, arg3);
    if (identical(arg5, NO_ARG)) return super.call(arg0, arg1, arg2, arg3, arg4);
    if (identical(arg6, NO_ARG)) return super.call(arg0, arg1, arg2, arg3, arg4,
        arg5);
    if (identical(arg7, NO_ARG)) return super.call(arg0, arg1, arg2, arg3, arg4,
        arg5, arg6);
    if (identical(arg8, NO_ARG)) return super.call(arg0, arg1, arg2, arg3, arg4,
        arg5, arg6, arg7);
    if (identical(arg9, NO_ARG)) return super.call(arg0, arg1, arg2, arg3, arg4,
        arg5, arg6, arg7, arg8);
    return super.call(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
  }
}

*它适用于 0-9 个参数 - 与 callsTo 相同

【讨论】:

    猜你喜欢
    • 2015-06-21
    • 2015-04-15
    • 1970-01-01
    • 2014-08-04
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多