我已经使用了 dart unittest 框架和所描述的包含的 Mock 类
然而,模拟调用中存在依赖关系,即:
..store.when(callsTo('isLocked')).thenReturn(false);
其中对isLocked
方法的调用是按名称调用的。如果有人重命名该isLocked
方法,重构框架将不会重命名该调用。
我想知道反射(镜子)是否可以以某种方式提供帮助,但我还没有找到解决方案。
干杯彼得
我已经使用了 dart unittest 框架和所描述的包含的 Mock 类
然而,模拟调用中存在依赖关系,即:
..store.when(callsTo('isLocked')).thenReturn(false);
其中对isLocked
方法的调用是按名称调用的。如果有人重命名该isLocked
方法,重构框架将不会重命名该调用。
我想知道反射(镜子)是否可以以某种方式提供帮助,但我还没有找到解决方案。
干杯彼得
作为一般规则,我尽量避免模拟类(除非它们使用外部资源,如 RESTful Web API),特别是因为这个问题。如果 A 类模拟了 B 类的 foo 方法,但 B 类实际上重命名了它的 foo 方法 fooBar,你的测试仍然会通过。但是,当 A 类尝试在运行时调用 B.foo 时,事情就会崩溃。因此,我避免嘲笑任何本地可用的东西。
幸运的是,在 Dart 中的情况比在其他动态语言中要好一些。例如,您可以创建一个实现另一个类的类。所以如果 FakeB 实现了 B,那么你可以在 FakeB 中添加可以在测试 A 时使用的函数。
自然,重构框架知道您的代码 callTo('isLocked') 实际上引用了一些名为 isLocked 的方法,而实际上该方法已重命名为 isReallyLocked,这有点过头了。但是,我认为您在查看镜像 API 时走在正确的道路上。例如,如果您可以编写 callTo(isLocked.name ),那就太好了。这样,VM 可以为您的模拟提供更多检查。
自然,真正的目标是在 API 不匹配时让测试失败。我玩了一些镜像 API,我能想到的最好的方法是:
#import('dart:io');
#import("dart:mirrors");
void a() {
print("a");
}
void main() {
String thisFile = "file://${new Directory.current().path}/${new Options().script}";
print(currentMirrorSystem().libraries[thisFile].functions['a'].simpleName);
}
这段代码仍然使用“a”作为字符串,但它的好处是如果“a”不作为函数存在,它会爆炸。这有点丑陋和骇人听闻,但它正朝着你所寻找的方向发展。
您可以做的另一件事是:
when(callsTo('isLocked')).alwaysCall(isLocked)
在这种情况下,模拟系统只是在调用者和 isLocked 函数之间扮演一个中间人。这种方法有两个缺点:a) 大概您试图完全避免调用 isLocked,否则您将不会使用模拟框架 b) isLocked 是重复的,首先作为字符串,然后作为函数。但是,它确实有几个好处:a)它可以让您记录 isLocked 被调用的事实 b)如果 isLocked 被重命名,它将不起作用;即重命名 isLocked 的人将看到此代码,并希望更新这两个地方。
另一种“蛮力”方法如下:
void a() {
print("a");
}
String makeSureItExists(obj, String name) {
return obj != null ? name : "NoSuchMethod";
}
void main() {
print(makeSureItExists(a, "a"));
}
这使您可以编写类似callsTo(makeSureItExists(login, "login")) 的内容。
好吧,我最终使用的方法是一个带有方法名称的常量类。像这样:
class MethodNames{
final String GET_ELEMENT = 'getElement';
final String GET_CLASSES = 'get classes';
final String SET_DISABLED = 'set:disabled';
final String AS_INPUT = 'asInput';
final String AS_BUTTON = 'asButton';
final String SET_INNER_HTML = 'set:innerHTML';
final String SET_VALUE = 'set:value';
...
const MethodNames();
}
我的优点是既漂亮又简单。我倾向于这样。
欢呼彼得