I find myself repeating a lot of the same test actions in my ICE Code Editor test suite. This seems a fine opportunity to explore test helpers in Dart unit tests.
The one in particular that I find myself doing a lot is clicking on element, usually with particular content:
queryAll('button').
firstWhere((e)=> e.text=='☰').
click();My first instinct is that the helpers should be a separate library that is imported into my test suite. The main reason is that I can import with an “as” prefix to make it patently obvious where the helper methods live. So, in my main ice_test.dart main test file, I add the import statement:library ice_test;
import 'package:unittest/unittest.dart';
// ...
import 'helpers.dart' as helpers;
import 'package:ice_code_editor/ice.dart';
main(){
// tests go here...
}
In helpers.dart, I start with a single click() function:library ice_test_helpers;
import 'dart:html';
void click(String selector, {text}) {
if (text == null) return query(selector).click();
queryAll(selector).
firstWhere((e)=> e.text==text).
click();
}The click function requires a string selector that will be used to query for elements to click. If no text is specified—if the optional, named parameter text is null—then I query for the first matching selector and click it. If the text parameter is specified, then I query for all matching selectors, find the first that contains the supplied text and click that. I continue to use the
firstWhere() because it will throw an exception if no matching element is found. I may want to bundle that into a new exception that makes it more obvious what has gone wrong in the test, but I leave it for now.With that, I can change the test that verifies one of the ways to close a menu:
test("the menu button closes the projects dialog", (){
queryAll('button').
firstWhere((e)=> e.text=='☰').
click();
queryAll('li').
firstWhere((e)=> e.text=='Projects').
click();
queryAll('button').
firstWhere((e)=> e.text=='☰').
click();
expect(
queryAll('div').map((e)=> e.text).toList(),
isNot(contains(matches('Saved Projects')))
);
});Instead, I can write that as: test("the menu button closes the projects dialog", (){
helpers.click('button', text: '☰');
helpers.click('li', text: 'Projects');
helpers.click('button', text: '☰');
expect(
queryAll('div').map((e)=> e.text).toList(),
isNot(contains(matches('Saved Projects')))
);
});Holy clearer intent Batman! It is much easier to see that this test clicks the menu button, then the Projects menu item, then the menu button again. I might have omitted the “helpers” prefix from my import statement and thus been able to treat
click() as a top-level function. I tend to think that the prefix will aid in long-term maintainability of the test suite as there will never be a question as to the source of the helper function. Day #757
No comments:
Post a Comment