I find that one of the things I like most while writing Dart unit tests for the forthcoming update to Dart for Hipsters is asserting things that will not work. It is a good way to verify that assertions that I make in the book are actually valid. More fun is when an old assertion proves to be no longer valid.
While talking about varying runtime behavior in Dart, I make the assertion that defining
noSuchMethod()
in a subclass prevents Dart from calling noSuchMethod()
in the superclass. For all I know, that could have changed since I wrote that paragraph. Even if it has not changed, it could very well change in the future so I really need a test to verify that assertion.In my test, I define a superclass
A
whose noSuchMethod()
throws an exception and a subclass B
whose noSuchMethod()
does nothing:import 'package:unittest/unittest.dart'; class A { noSuchMethod(args) { throw new NoSuchMethodError(); } } class B extends A { noSuchMethod(args) {} } run() { group("[noSuchMethod]", (){ test('cannot access noSuchMethod in superclass when defined in subclass', (){ var b = new B(); expect(()=> b.foo(), returnsNormally); }); }); }In the test, I have created an instance of
B
and invoked a non-existent method foo()
that should find its way into noSuchMethod()
. And indeed, it does hit noSuchMethod()
because the expectation in that test that invoking the foo()
method returns normally proves to be correct:➜ varying_the_behavior git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done PASS: [noSuchMethod] cannot access noSuchMethod in superclass when defined in subclass All 1 tests passed.If
noSuchMethod()
in the subclass had not been invoked, then Dart would have thrown an error. If the noSuchMethod()
method had been invoked in the superclass, the superclass would have thrown an error. Since the method returns normally, I am hitting noSuchMethod()
in the subclass as desired.So the text is correct and I can move on right? Maybe, but then again, maybe not.
The args that are being passed into
noSuchMethod()
are not a list of parameters—they are an InvocationMirror
object. Perhaps there is a way to invoke the superclass's noSuchMethod()
with this InvocationMirror
doohicky.The only aspect that seems to have potential in
InvocationMirror
is the invokeOn()
method. But I cannot actually figure out what to invoke invokeOn()
... on. If I try it on this
, I get stack overflow:class C extends A { noSuchMethod(args) { args.invokeOn(this); } } // ... test('can access noSuchMethod in superclass when defined in subclass', (){ var c = new C(); expect(()=> c.foo(), throwsNoSuchMethodError); }); // ...This results in the aforementioned stack overflow:
➜ varying_the_behavior git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done FAIL: [noSuchMethod] cannot access noSuchMethod in superclass when defined in subclass Expected: throws an exception which matches NoSuchMethodError but: exception <Stack Overflow> does not match NoSuchMethodError.And I cannot
invokeOn()
on super
:class C extends A { noSuchMethod(args) { args.invokeOn(super); } }Dart will not even compile that:
➜ varying_the_behavior git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done 'file:///Code/varying_the_behavior/test/superclass_no_such_method.dart': Error: line 18 pos 24: illegal use of 'super' args.invokeOn(super); ^And then it occurs to me: I want the
noSuchMethod()
method in the superclass to be invoked with the InvocationMirror
that I already have. Perhaps I can call super.noSuchMethod()
and supply my args
InvocationMirror
to it:class C extends A { noSuchMethod(args) { return super.noSuchMethod(args); } }It seems that I can do that as my test now passes:
➜ varying_the_behavior git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done PASS: [noSuchMethod] can access noSuchMethod in superclass when defined in subclass All 1 tests passed.This even works with parameters. I update the super class to extract sum parameters passed to the non-existent "bar" method then multiply the result by 2:
class A { noSuchMethod(args) { if (args.isMethod && args.memberName == "bar") { return 2 * args. positionalArguments. reduce(0, (prev, element) => prev + element); } throw new NoSuchMethodError(); } }I then write a test that invokes the
bar()
method on the subclass: test('can pass parameters to noSuchMethod in superclass', (){
var c = new C();
expect(c.bar(2,2), equals(2*(2+2)));
});
And it passes:➜ varying_the_behavior git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done PASS: [noSuchMethod] can access noSuchMethod in superclass when defined in subclass PASS: [noSuchMethod] can pass parameters to noSuchMethod in superclass All 2 tests passed.Now if you'll excuse me, I have a chapter to rewrite...
Day #624
No comments:
Post a Comment