So there is a lot of buzz going on around testing JavaScript code and around doing TDD and a lot of people are writing blog pots on how to write tests with a couple of different frameworks like jasmine, jstestdriver, jstest, therefore they’re not really describing the main purpose, the main focus about it nor why, where and when you should write tests for JavaScript, which can lead to a big mistake for the first time tester/reader since adding tests to something that is not going to increase the value of your code is not a good way of writing tests.
JavaScript is on web development for a long time and it has been used for many different purposes, but recently the web development community discovered that JavaScript is becoming part of the core features which any web application must have, especially if the app needs some level of user interaction or user experience. What most people don’t know though, or at least turn a blind eye on it, is that if JavaScript is that important to the app being built, it will sure add some behavior to it and this is bad because I assume that every developer knows, or at least should know that if you have any code that will affect your application behavior you should have tests for it.
That is sadly the most common error I’ve seen in most of the projects that uses JavaScript: developers don’t write tests for something if he or she underestimate the potential change in the app that the code can lead to, e.g. sometimes a developer just say “Ok I’ll just bind this click” or “I’ll just hide this and them show again” and ignore the fact that this simple actions will add some kind of behavior to the flow of the app, the potential changes that this may lead to the app has been underestimated and the code that follow this way of thinking will become lost and not maintainable for a long time until someone finds it and finally discover what it means or just delete because it has no use anymore, not to mention the many hours lost trying to find the code in case it leads to or has been involved in a bug.
So how to tackle that? writing tests for the browser behavior like clicks or showing hiding stuff is annoying since you have to mock the whole page. The solution that me and the folks from our project found very useful is to have objects that will represent the behavior for that specific scenario, it’s kinda of a Model View Presenter / Passive View but a little bit more intrusive on the Model since the model isn’t the actual Domain Model and it’s a middle point between the Domain and the View. Using this pattern we found easy to write tests around the Controller and the JavaScript Model itself and we don’t need to mock the whole page.
One real quick example that I have in mind from one of our (me and Rafael Bandeira @rafb3) discussions about how to write tests for JavaScript is regarding showing images in a carousel widget. The scenario was something like “I have a couple of images on the page and I have to get some of those images based on a business rule and show in the carousel”. Our idea to solve this problem and have tests was to create an object ImagesCollection and populate it with all the images via another object CarouselController and all of it would be implemented with JavaScript. The Images collection itself would have Image objects that the CarouselController would be able to manage and based on the Domain Model it would be able to set it to be visible or not. The CarouselController would be also responsible to load or unload the carousel widget and populate it with the images that were supposed to show there.
By doing it, these are the behaviors that we were able to test:
- The Domain Rules being applied to the Image object to be visible or not. (Image object)
- The creation of the collection containing all the images in the page. (CarouselController responsability)
- The filtering and querying of the images based on a criteria. (ImagesCollection responsability)
- The management of the carousel widget and the collection, which was achievable by quick mocking the ImagesCollection and the widget. (CarouselController responsability)
So yeah, all the behaviors we wanted were tested and will be easy to maintain in the future. There’s a couple o things that need to be mentioned though:
- Since we created an intermediate for the Domain Model, aka. JavaScript Model, we need tests to be sure that this model actually reflect the Domain Model rules otherwise you can end up having models with different rules and kinda lose control of your business.
- Despite in this way you’re going to have tests to your JavaScript it doesn’t mean that the browser will actually behave like your tests want to, this means that even though we wan’t things to be on the screen (like the carousel) it doesn’t mean that it will be there because this depends on other variables like styles, server calls, browser specific issues, etc. and this stuff can be captured with other levels of testing like functional tests or even manual exploratory tests, like said before you can actually write your JavaScript tests to achieve this (mocking the whole page), but in my opinion it’s painful and this pain doesn’t worth the result.
Having these things in mind I hope you’ll be able to write valuable JavaScript tests. Another thing that I wanted with this post is to show that JavaScript testing isn’t just about using one or other tool/framework, despite that I do prefer jasmine. JavaScript testing is (as any other test/language) more about how much do you want your code maintainable and how quick you want to have feedback about your UX code.
By having JavaScript tests in the right place for the correct features you not only increase your code maintainability but also reduce the time in which you receive feedback for the changes you made. It doesn’t mean that if you don’t write JavaScript tests you’re with big problems… it means that by not writing JavaScript code you’re not going to get the feedback for your changes anytime soon than your end user testing. To an agile project this time, the maintainability and the quick feedback is valuable.