This is post #3 of 3 to explain about fsstatic2. Beware: it may change forever how you approach web development. :-)
Let’s finish this.
docs.html – A component catalog AND a playground environment.
When working with AngularJS, it’s very useful to have a “playground area” where you can create components without necessarily changing the behaviour of the main application. The DOCS environment serves this purpose. It’s a collection of “test pages” which are able to showcase its own code. It’s very easy to add a new test page, let me show you:
This environment actually can serve multiple purposes:
Playground area: using this environment on their local machines, developers on your team can develop and test components individually. It’s very advisable that every component have its own test page.
Component documentation catalog: If you publish this application on the internet, people can check it out whenever they need to see which components are available, and how to use them.
Prototype showcase: Developers can show their work in progress to designers, clients, and stakeholders in general, in order to collect feedback and refine the direction the product is supposed to go.
So, this is another good practice:
Good practice #7: Have a component catalog.
Tests: running, debugging and coverage reports
Tests should go in the docs folders and be called “test_*.js”. Now there are a few things you want to be able to do with them, like:
- Run them all [runtests --singleRun=true]
- Watch for changes, and keep running [runtests]
- Run a single test [runtests --grep=blabla]
- Debugging [runtests, open localhost:9876/debug.html]
- Generate test coverage report (sorry, forgot about this in the video) [runtests --coverage=true]
The video below shows you how
test_todo.js: how to write good tests
If you follow Good practice #5 you’ll end up with a codebase where most of your code will be inside AngularJS services. Well, this is *very* good for testing – among all the types of objects you can create with Angular, services are the easiest to test.
Take a look at our test (test_todo.js). I could have made that test use TODOModel directly, but instead I created another service TODOTester. When testing viewmodels like TODOModel, I found out that using a “tester service” like that allows me to write more expressive tests. I mean look at this code:
TodoTester.assert_count_todos(0); TodoTester.type_and_add('one todo'); TodoTester.type_and_add('to twodos'); TodoTester.type_and_add('tdee throdos'); TodoTester.assert_count_todos(3); TodoTester.remove(1); TodoTester.assert_count_todos(2); TodoTester.assert_todo_text(1, 'tdee throdos');
It really looks like we’re simulating a user ”driving” the application. You can even write the test first, give empty implementations to TodoTester methods – your tests will pass, then add the method bodies later. This gives you an incredible “TDD feeling” when you’re doing it.
Another detail worth mentioning is that the tests include api_mock.js, not api.js. This means you can reuse the same mock implementations you already have in your tests (and this is much much better than mocking ajax calls with $httpBackend). For example, we do that in TodoTester.type_and_add(). We know TODOModel.add() will make a (fake) ajax call, and all we need to do is to force our fake backend to respond by calling $timeout.flush().
expect(count).equal(TODOModel.todos.length); //still waiting for the "server" to respond $timeout.flush(); //yay, the server responded! expect(count + 1).equal(TODOModel.todos.length);
Here’s another tip that may help you with your tests. You could create additional methods on the fake FSApi service (say, FSApi._set_todos(array)), which are only supposed to be used inside tests or DOCS sample pages, which would allow you to preload data on the “fake database”. Speaking more generally, you can add methods to your fake apis that change your fake backend’s behaviour. You can use those methods to load different scenarios both inside unit tests and test pages.
We talked a lot about tests and the good practice here is not so obvious this time:
Good practice #8: ViewModel tests should be talking in a user’s vocabulary.
You should think “I’m gonna write a test for requirement/scenario X” much more often than “I’m gonna write a test for method X”. High test coverage should be only a consequence of that approach. Most of the time this means having to create a “tester” service with method names that are very close to user actions. Sometimes you may also feel the need to create other types of test-helping services, like a “ScenarioLoader”. In any case, the code inside the test itself, should be “easy” enough for even non-programmers to understand.
What we’ve seen here is more than a project’s setup. I believe that the techniques and ideas presented here can be applied to any web application project to improve architecture/code quality, maintainability, testability, development speed, and as a consequence, happinnes, for you and your team :-)
Perhaps this is a lot to remember, and if so, the list of good practices below will help, hopefully.
- #1: Have a project help on the command line (help your team and your future self)
- #2: Everything is a component (which uses other components)
- #3: Have a mock API (with promises created out of $timeout)
- #4: Plan for some flexibility in your templateURLs (with some global variable as a prefix)
- #5: NEVER use the $scope as a view model. Put your models into services (this is very important!)
- #6: Let me see the API first (helps answer the “What does this do?” question faster)
- #7: Have a component catalog
- #8: ViewModel tests should be talking in a user’s vocabulary (and you’ll need to create extra services for this)
OK, that’s all I had to say folks. Hope it helps.