-
Notifications
You must be signed in to change notification settings - Fork 310
Start writing unit tests #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I've just pushed an additional pair of commits that switches from the old |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, cool about switching to the new checks
package! I'd written some comments before those commits came in, so please disregard the ones that don't apply anymore.
test/model/content_matchers.dart
Outdated
// TODO surely this is common boilerplate we can avoid, right? | ||
// ... Well, doesn't seem like the matcher package has a way: | ||
// https://pub.dev/documentation/matcher/latest/matcher/matcher-library.html | ||
// Maybe try its proposed successor which is in beta?: | ||
// https://pub.dev/packages/checks | ||
if (item.nodes.length != nodes.length) return false; | ||
for (var i = 0; i < nodes.length; i++) { | ||
if (!nodes[i].matches(item.nodes[i], matchState)) { | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might give the same result?
if (!equals(nodes).matches(item.nodes, matchState)) {
return false;
}
Here's equals
; see the third paragraph:
https://pub.dev/documentation/matcher/latest/matcher/equals.html
/// Returns a matcher that matches if the value is structurally equal to
/// [expected].
///
/// If [expected] is a [Matcher], then it matches using that. Otherwise it tests
/// for equality using `==` on the expected value.
///
/// For [Iterable]s and [Map]s, this will recursively match the elements. To
/// handle cyclic structures a recursion depth [limit] can be provided. The
/// default limit is 100. [Set]s will be compared order-independently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If [expected] is a [Matcher], then it matches using that. Otherwise it tests /// for equality using `==` on the expected value.
Yeah — I was fooled by the name and summary, which seemed awfully clear that it meant equal and not recursively applying general matchers.
It turns out the checks
package has a similar story, but perhaps there's an opportunity to improve it there before it's stable.
lib/model/content.dart
Outdated
@@ -2,8 +2,12 @@ import 'package:flutter/foundation.dart'; | |||
import 'package:html/dom.dart' as dom; | |||
import 'package:html/parser.dart'; | |||
|
|||
// TODO: Implement ==/hashCode for all these classes where O(1), for testing/debugging | |||
// (Skip them for classes containing lists.) | |||
// TODO: Implement toString for all these classes, for testing/debugging |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Flutter style guide has a note about choosing between toString
and Diagnosticable
: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#override-tostring
Would Diagnosticable
be useful for some of the classes, do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely. I haven't yet read up on Diagnosticable
. Should probably have a TODO comment for it.
|
||
@override | ||
bool operator ==(Object other) { | ||
return other is LineBreakNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LineBreakNode
is unlikely to be subclassed, I think, but isn't that a factor in choosing this implementation with is
over one that equality-checks the runtimeType
s? The operator ==
example in the Flutter project's style guide compares runtimeTypes
:
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
// […]
}
If we have a class Animal
that overrides ==
, and its subclasses Dog
and Cat
that don't, then someDog == someCat
will give
- true if
Animal
's==
override isother is Animal
- false if
Animal
's==
override isother.runtimeType == runtimeType
(False is probably the right answer.)
(code that you can paste into dartpad.dev)
class Animal {
@override
bool operator ==(Object other) {
return other is Animal;
}
@override
int get hashCode => 'Animal'.hashCode;
}
class Dog extends Animal {}
class Cat extends Animal {}
Dog someDog = Dog();
Cat someCat = Cat();
main() {
print('someDog == someCat: ${someDog == someCat}');
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it's worth changing the implementation or commenting about the expectation that it won't be subclassed. Am I being too paranoid? 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and:
LineBreakNode
is unlikely to be subclassed, I think
Is this right? If so, can I make the same judgment of the other non-abstract
FooNode
classes in the file (so e.g. LineBreakInlineNode
but not EmojiNode
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, none of these classes should ever have subclasses beyond the ones defined in this file. (So in Dart 3, we'll declare them as sealed
: https://github.com/dart-lang/language/blob/a374667bcc4095edd22e2e6d454cbe9d9f9d85fa/accepted/future-releases/sealed-types/feature-specification.md .) For most of them, including LineBreakNode
, that means they'll have no subclasses at all.
|
||
@override | ||
bool operator ==(Object other) { | ||
return other is TextNode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Same comment about the is
operator.)
test/model/content_test.dart
Outdated
parseContent('<p>hello world</p>'), | ||
ZulipContentMatcher([ | ||
ParagraphNodeMatcher( | ||
wasImplicit: equals(false), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly this could be made more compact by letting the caller choose between a matcher and a literal value (with the equals
matcher implied), so you could pass wasImplicit: false
instead of wasImplicit: equals(false)
. Then internally ParagraphNodeMatcher
could use wrapMatcher
.
I see your inline responses to my review; please merge at will (perhaps after adding a TODO for |
Definitely spiffier than `matcher`. Has useful static types, and also a better API for producing clear diagnostics on failure.
When working on #13 I realized that storing data means we're going to start definitely wanting tests.
So here's some first steps in having them, which provides the common infrastructure that tests for #13 will also want.