dart - 模拟,名称调用的方法名称,重构失败

时间:2012-09-12 10:44:26

标签: mocking dart dart-mirrors

我使用了dart unittest框架和包含的Mock类as described

然而,模拟调用存在依赖性,即:

..store.when(callsTo('isLocked')).thenReturn(false);

通过名称调用isLocked方法的调用。如果有人重命名了isLocked方法,重构框架将不会重命名该调用。

我想知道反射(镜像)是否能以某种方式提供帮助,但我还没有找到解决方案。

干杯 彼得

2 个答案:

答案 0 :(得分:1)

作为一般规则,我试图避免模拟类(除非他们使用外部资源,如RESTful Web API),因为这个问题。如果A类模仿了B类的foo方法,但是B类实际上已经重命名了它的foo方法fooBar,那么你的测试仍然会通过。但是,当A类试图在运行时调用B.foo时,事情就会崩溃。因此,我避免嘲笑当地可用的任何东西。

幸运的是,Dart的情况比其他动态语言要好一些。例如,您可以创建一个实现另一个类的类。因此,如果FakeB实现B,那么您可以向FakeB添加可在测试A时使用的函数。

当然,重构框架知道您的代码调用To('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”不作为函数存在,它将会爆炸。它有点丑陋和hacky,但它正朝着你想要的方向发展。

你可以做的另一件事是:

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"));
}

这可以让你写一些类似于callTo(makeSureItExists(login,“login”))。

答案 1 :(得分:0)

好吧,我最终使用的方法是一个带有方法名称的常量类。像这样:

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();
}

我的优点是简单明了。我倾向于那个。

欢呼声 彼得