Friday, December 14, 2012

Organizing Dart App Code for Testing

‹prev | My Chain | next›

Up today I hope to figure out how to test a "real" Dart application. I have been using the unittest library to test some of the client-side code that I built to support Dart for Hipsters, but the code in question was not laid out to use packages from Dart Pub. That is, the code was placed directly in public/scripts rather than in public/scripts/web. Furthermore, I have not needed to account for pub packages in my tests so far—it all existed directly in public/scripts.

The code layout, including tests, that I now have in my sample app looks like:
├── packages
│   ├── dirty -> /home/chris/.pub-cache/hosted/
│   ├── unittest -> /home/chris/.pub-cache/hosted/
│   └── uuid -> /home/chris/.pub-cache/hosted/
├── public
│   ├── images
│   ├── index.html
│   ├── scripts
│   │   ├── packages
│   │   │   └── hipster_mvc -> /home/chris/.pub-cache/hosted/
│   │   ├── pubspec.lock
│   │   ├── pubspec.yaml
│   │   └── web
│   │       ├── Collections.Comics.dart
│   │       ├── main.dart
│   │       ├── Models.ComicBook.dart
│   │       ├── packages -> /home/chris/repos/dart-comics/public/scripts/packages
│   │       ├── Views.AddComicForm.dart
│   │       └── Views.Comics.dart
│   └── stylesheets
├── pubspec.yaml
└── test
    ├── HipsterCollectionTest.dart
    ├── index.html
    ├── packages -> /home/chris/repos/dart-comics/packages
    └── Views.AddComicForm_test.dart
I had been placing my client-side tests in the root-level test directory. Now that my client-side Dart code resides in public/scripts/web instead of public/scripts and, more importantly, now that the Hipster MVC classes are in the Pub's packages directory, the root level test directory will no longer work. I get errors like the following:
Failed to load a file file:///home/chris/repos/dart-comics/test/packages/hipster_mvc/hipster_view.dart Views.AddComicForm_test.dart:-1
GET file:///home/chris/repos/dart-comics/test/packages/hipster_mvc/hipster_view.dart  index.html:17
The problem is in the Views.AddComicForm.dart file:
library add_comic_form_view;

import 'dart:html';
import 'package:hipster_mvc/hipster_view.dart';

class AddComicForm extends HipsterView {
  // ...
The import of the hipster_view.dart library is looking in the top-level packages directory since the test resides in the top-level test directory. But the only packages that reside in the top-level packages directory are those that support the Dart web server.

I had resisted placing my tests in the public/test directory because then they would be in the public (i.e. publicly available) directory. But now it seem that I may not have a choice.

So, in the public/scripts directory, I add a test sub-directory and run pub install:
➜  scripts git:(master) ✗ mkdir test 
➜  scripts git:(master) ✗ pub install
Resolving dependencies...
Dependencies installed!
➜  scripts git:(master) ✗ ls -l test
total 0
lrwxrwxrwx 1 chris chris 53 Dec 14 23:15 packages -> /home/chris/repos/dart-comics/public/scripts/packages
This creates a link to the pub packages directory, so I seem to be on the right track.

I move my client-side tests from the root level into this new public test directory and adjust the import paths accordingly:
import 'package:unittest/unittest.dart';
import 'package:unittest/html_enhanced_config.dart';

import '../web/Views.AddComicForm.dart';

main() {
  // ...
With that, I have my tests passing again, now from the public directory:

I suppose the test code in the public directory is not a huge deal. Still it seems wrong to allow test code to be publicly available from the web server. I could use something like an .htacess file to prevent access to the test sub-directory, but that too seems less than ideal.

Instead, I move my application code, my tests and the associated pubspec.yaml file outside of public. For now, I place it all in a new directory named appcode in the application root. After re-running pub install, the appcode sub-directory looks like the following:
➜  appcode git:(master) ✗ tree   
├── packages
│   ├── hipster_mvc -> /home/chris/.pub-cache/hosted/
│   └── unittest -> /home/chris/.pub-cache/hosted/
├── pubspec.lock
├── pubspec.yaml
├── test
│   ├── HipsterCollectionTest.dart
│   ├── index.html
│   ├── packages -> /home/chris/repos/dart-comics/appcode/packages
│   └── Views.AddComicForm_test.dart
└── web
    ├── Collections.Comics.dart
    ├── main.dart
    ├── ModalDialog.dart
    ├── Models.ComicBook.dart
    ├── packages -> /home/chris/repos/dart-comics/appcode/packages
    ├── Views.AddComic.dart
    ├── Views.AddComicForm.dart
    └── Views.Comics.dart
The trick here is that, publicly, I only need the stuff in the web sub-directory. Well, I also need the pub packages, but I have them in the web sub-directory by virtue of the packages symlink that points to an absolute path. In other words, I can symlink just the web sub-directory into public:
➜  appcode git:(master) ✗ pwd
➜  appcode git:(master) ✗ cd ../public/scripts 
➜  scripts git:(master) ✗ ln -s ../../appcode/web dart_comics
And that works. I still have my test suite passing, but now it is only accessible from a local filesystem. And, I still have my application working:

After considering it for a bit, I believe that this (or something like this) is what Dart Pub wants me to do. That is, I believe that including only the web sub-directory in a public folder is how Dart applications should be served up. The web sub-directory could come from an appcode sub-directory as I have done here. More interestingly, it could come a pub package—possibly from a git repository.

I think that is an idea worth pursuing a bit more. Tomorrow.

Day #599

No comments:

Post a Comment