我如何去模拟一个程序(与函数相关联见here)
例如,给定以下typedef和过程,
typedef int Adder(int a, int b);
int useAdder(Adder adder) {
return adder(1, 2);
}
你怎么能写一个允许你测试一个名为你的userAdder程序模拟函数的模拟?
这是我的尝试,但它失败并显示测试失败的消息:Caught null对象没有方法' 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);
方法调用未出现在日志中
答案 0 :(得分:2)
我的尝试
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。
答案 1 :(得分:2)
我使用GünterZö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