Wednesday, May 29, 2013

Many, Many Dart Test Parts

‹prev | My Chain | next›

The test suite in the Dart version of the ICE Code Editor is a slave to the JavaScript included in it. And it is making my test suite messier than I would like. Unfortunately, I do not think that I have much choice in the matter.

Actually, I do have a choice. I am intentionally hiding the JavaScript nature of ICE from developers. I want them to be able to create an instance of ICE with just a couple lines of Dart and no icky JavaScript:
<head>
  <script type="application/dart">
import 'package:ice_code_editor/ice.dart' as ICE;

main()=> new ICE.Full();
  </script>
</head>
Since there is JavaScript in ICE (the actual code editor is ACE code editor), this means that I have to dynamically add <script> tags to pull in the JavaScript.

This works quite well in the application code, but is a mess in the test suite. I have code that prevents multiple <script> tags from being added for ACE (yes, it is a bit confusing with ICE and ACE—I chose ICE before I had even heard of ACE). But the preventative duplicate <script> tag is the only nod that I give to testing in the application code.

It is never a good idea to optimize for the testing environment, so I do not intend to do so today. But today may be the day that the current approach gets out of control. Currently, the test suite looks like:
library ice_test;

// import libraries for testing here...

part 'editor_test.dart';
part 'store_test.dart';
part 'gzip_test.dart';
part 'full_test.dart';

main(){
  editor_tests();
  store_tests();
  gzip_tests();
  full_tests();
}
I want to extract the dialog tests out of full_test.dart, which is currently 561 lines of code and nigh impossible to search for the group that I want.

I pull the copy-related tests out into a copy_dialog_test.dart file:
part of ice_test;

copy_dialog_tests() {
  group("Copy Dialog", (){
    var editor;

    setUp(()=> editor = new Full(enable_javascript_mode: false));
    tearDown(() {
      document.query('#ice').remove();
      new Store().clear();
    });

    test("can open copy dialog", (){
      helpers.click('button', text: '☰');
      helpers.click('li', text: 'Make a Copy');

      expect(
        queryAll('button'),
        helpers.elementsContain('Save')
      );
    });
    // ...
  });
}
As the opening declaration indicates, I make this part of the whole test suite rather than a smaller library. I found it best to keep all of the code in the same execution isolate—again mainly due to js-interop concerns. Since this is part of the whole ice_test library, I need to mark it (and the other dialog tests) as a part, and run the test function:
library ice_test;

// imports...
part 'editor_test.dart';
part 'store_test.dart';
part 'gzip_test.dart';

part 'full_test.dart';
part 'full/copy_dialog_test.dart';
part 'full/share_dialog_test.dart';
part 'full/open_dialog_test.dart';
part 'full/new_project_dialog_test.dart';
part 'full/rename_dialog_test.dart';
part 'full/save_dialog_test.dart';
part 'full/remove_dialog_test.dart';

main(){
  editor_tests();
  store_tests();
  gzip_tests();

  full_tests();
  copy_dialog_tests();
  share_dialog_tests();
  open_dialog_tests();
  new_project_dialog_tests();
  rename_dialog_tests();
  save_dialog_tests();
  remove_dialog_tests();
}
I am not thrilled at the large number of parts. That said, it is not as horrible as I had feared. All 50+ tests still pass. The complexity of the test organization is handled in one file—albeit in two places. Even if I coud split this test suite out into libraries, the full tests would likely be in the same library and it would not end up much cleaner.

Overall, I think this approach is probably worth the parts hassle (and, really, it's not much of a hassle). Instantly knowing exactly where all copy-dialog tests (or any other dialog tests) are located is going to help immensely.


Day #766

No comments:

Post a Comment