Along the same line... Any good resources on building maintainable applications with user interfaces? It doesn't have to be web applications, it can be a book focused on building desktop or mobile applications. I'm more interested in the patterns the follow, as well as what and how they focus on testing.
I've found TDD pretty much impossible when you're building a user interface. Even when I test my components (recently using react, prior to that using angular), it's hard to know what I should be testing for. Testing the component's logic is a given, and usually very straightforward. But how in depth should my DOM structure validation be? Should I check that certain classes and IDs are set correctly and leave it at that? Should I verify the DOM structure of the component? I've found this extremely difficult because you'll often find yourself adding or removing classes to restyle components, or you'll find yourself rewriting the HTML to fit better with a redesign of some sort.
Along this same line, what about e2e tests? I test core user interactions (e.g. user can create foo) with e2e tests, and edge-cases with unit tests. These tests will be clicking around and sending keys to inputs and eventually reach some state that confirms that the action was successful. Among all the states that were traversed, how many things will you validate (e.g. check that the link the user clicks is in a list, check that in the final state he can view all of foo's information)? And how in depth? (e.g. checking that #foo contains all of foo's information, or checking that #foo contains .bar, .baz, .qux, and that each contains part of foo's information)
I've figured out some patterns and guidelines over time, but some of these tests tend to feel brittle or useless.
I have a screencast on developing a front-end JavaScript app that covers this topic. [1] The short answer is that you approach it in the same way you approach other TDD challenges: think about the code you want to write, then write a test that fails until that code has been written, then improve it, then repeat. A good test will talk more about the observable behavior of the unit rather than the way it's implemented.
So if your production JavaScript code sets a class based on some piece of business logic, your test checks that it sets that class in that situation. That may mean doing some DOM element creation in your test setup.
Testing HTML and CSS is a bit trickier. I'm developing a tool called Quixote [2] that enables TDD of this sort of code. It lets you do things like say "the login button needs to be 20px to the right of the nav bar." Same deal: you think about what you want your CSS to do, write the test, then write the CSS, then improve it.
Regardless, these are unit tests (or unit-like tests; I don't want to argue semantics), not end-to-end tests. Each one is focused on testing a specific thing, stay inside the browser process, and you use them to drive development. The key is to use good tools; I like Karma [3] because it allows me to run my tests against multiple browsers simultaneously, including mobile devices.
And, with apologies to @Anchor, you should absolutely test using real browsers. There are meaningful differences between browsers and you want your tests to catch those differences. Speed is not a problem if you use good test design and tooling; my Quixote tests use Karma, run against ten browsers, and execute 200 tests / sec.
Forgive me for being vague and brief, but I've been trying to test my UIs for the last year or so and this is what I've come up with.
I've found the ideas in Gary Bernhardt's talk, Boundaries (1), to be very helpful in figuring out how to test interactive web UIs. The basic idea is that DOM manipulation can be an "imperative shell" with as little logic and as few conditionals as possible. Derive the state of your UI component with separate functions and methods that return plain values (a "functional core"), and write tests for those because testing for values is easy.
So no, don't test for the presence of classes and IDs. Test your imperative shell once, maybe even manually or with an integration test, and that's probably enough.
Angular and React both encourage something like this approach, but you don't need those technologies to do it this way. In Backbone, for instance, just try to avoid conditionals and logic in your templates and render methods. If you think your UI piece needs to render with conditionals because it can be in different states, ask yourself if those states are just appearances that can be programmed declaratively with classes and CSS.
Also, testing is so much easier when you limit API surface areas. So if you're using a library like Backbone, where models and views both have many methods with many ways they can be called and used, don't give them direct references to each other. I've had some success in having models and views communicate only with a global event bus, which means my objects don't even know about each other. This makes them easier to specify, which makes them easier to test, which usually results in more focused interfaces and responsibilities.
I often plan large-scale UI code as three fundamental levels:
1. Model data
2. View data
3. Rendered data
The model data comes from whatever underlying data store and business rules you are working with.
The view data contains every value you need to render your UI. This typically includes a lot of data taken straight from the model. However, it can also include derived data, perhaps the result of some arithmetic calculation or the outcome of some conditional logic. This is also the level where any view metadata that isn’t persisted in the model lives, for example if you need to keep track of a cursor position, zoom level, contents of a clipboard/kill ring, etc.
The rendered data level only applies if you’re creating your UI using a descriptive/declarative system rather than by calling some sort of API. For example, for a web app, the rendered data would be HTML ready to put into the DOM. At this level, I try to keep the logic trivial; you might need some basic conditional or loop logic in the rendering, but the view data is where anything complicated happens, and the rendering logic in something like an HTML template shouldn’t be more complicated than “if (simple boolean value provided by view data) then (version A) else (version B)” or “for each item in (list provided by view data) do (render individual item)”.
Ideally, the conversions from model data to view data and from view data to rendered data are pure (as in without side-effects) functions, and therefore in principle they are amenable to automated testing techniques in isolation. For example, it is straightforward to add unit tests that calculated view data do have the expected values for sample model data.
In practice, I find automated testing of the view data to rendered data stage has very little value, for two reasons. Firstly, if your rendering stage is basically just filling out templates using view data and the logic is trivial, errors tend to be obvious: a table has no contents, for example, or an entire part of the page disappears. Secondly, my experience is that most of the bugs I find at the rendering level don’t actually originate in the rendering logic. Rather, they tend to be in some accompanying data, such as a CSS stylesheet that didn’t include the right prefixes or got the media queries wrong, or as a bug in the rendering engine itself that is out of your immediate control, such as a browser layout engine bug.
Edit: Of course, the above only describes data going one way through the system. Depending on the application, you might also have interactions that update view meta-data, and in anything beyond pure visualisation code you’ll surely have interactions that need to update the model data. This is also amendable to automated testing, as long as you have reasonable separation between (a) the code that does things like validation, constraint checks and eventually state modification at the model and view levels, and (b) whatever event-handling or other code starts the process. In this case, it’s that event-handling or other trigger code that is the part where bugs tend to be either obvious or outside of your immediate control, and you can have unit test suites for everything below. You can also use tools like Selenium to simulate those initial interactions and test in a more end-to-end fashion in real browsers.
Here is a good introduction to structuring applications https://www.youtube.com/watch?v=HhNIttd87xs The talk is given by Robert C. Martin (Uncle Bob) and, quite naturally, also touches the issue of testing.
Much of this comes down to decoupling different aspects of the system: persistence (DB), domain logic (business rules), delivery mechanism (web, REST), presentation format, UI layout, etc. This aggressive decoupling enables one to isolate and test these different facets of the system individually. Of course, building this decoupling requires some effort, and one has to be careful not to over-engineer. As with many things, it is a balancing act.
And yes, you can do TDD with the UI; separate the UI logic (user actions, UI state, input validation, etc.) from the layout system (and don't try to test the latter automatically). The fun thing is that when the system is decoupled, you can test the user interface of a JavaScript web application without ever starting a real* browser, running a web server or spinning up a database. It is an interesting system design challenge to be able to run even your UI tests at a rate of 100 tests/second.
*A headless browser is not a real browser in this sense
Full disclosure - I'm part of Applitools' core team.
Since the service we provide is visual testing, we work with a lot of web development teams and automation teams. I've encountered different levels of unit tests and e2e tests and I can't say there's a single methodology to "rule them all". Some test only specific pages, some browse the entire website, some load specific components to a separate page for unit testing, and some don't. Some have their R&D team provide "id" to each element (which is set "forever") so the automation team has a very easy time locating elements, and some have to "make do". So on that matter, I think you should go with whatever gives you the best ROI.
The questions "which elements to check?" and "how many things to validate?" are exactly the questions for which there's a really good and simple answer: use visual testing and validate everything with a single call. Got a signup page with multiple options and inputs? awesome. use visual testing on the page and voila, all the elements and data in the page are validated, no element-specific validation code required. In fact, you can flow through your application, use visual test on every page (or major event), and boom! You got dozens if not hundreds or more elements validated in a single swoop.
Applitools ( https://www.applitools.com ) does an extremely good work in making visual testing trivial, so you can have full blown E2E validation on your website/webapp with almost no validation code actually written. It's worth trying us out. We allow for a free account, and our SDKs are open source.
There are quite a few visual testing tools, for example WebdriverCSS (https://github.com/webdriverio/webdrivercss), which is an open source visual testing framework built on top of WebdriverIO.
Visual testing tools actually differ quite a bit from one another in many ways (automation framework integration, if any, baseline management, comparison engine etc).
Sort of a side note but: this is one of the cool things about react. Testing dom structure and things like ids and classes becomes (practically) testing react itself and redundant. If you keep components simple and driven entirely by props and state (perferably as little state as possible), you only need to verify these things are what you'd expect them to be for the given state in the state machine that is your component. Its beautifully simple. You can even get a long way with tools like react-vdom where you don't actually render anything and just test the validity of your props. https://github.com/gcanti/react-vdom (I believe the author did tcomb...definitely also use jest though)
I've found TDD pretty much impossible when you're building a user interface. Even when I test my components (recently using react, prior to that using angular), it's hard to know what I should be testing for. Testing the component's logic is a given, and usually very straightforward. But how in depth should my DOM structure validation be? Should I check that certain classes and IDs are set correctly and leave it at that? Should I verify the DOM structure of the component? I've found this extremely difficult because you'll often find yourself adding or removing classes to restyle components, or you'll find yourself rewriting the HTML to fit better with a redesign of some sort.
Along this same line, what about e2e tests? I test core user interactions (e.g. user can create foo) with e2e tests, and edge-cases with unit tests. These tests will be clicking around and sending keys to inputs and eventually reach some state that confirms that the action was successful. Among all the states that were traversed, how many things will you validate (e.g. check that the link the user clicks is in a list, check that in the final state he can view all of foo's information)? And how in depth? (e.g. checking that #foo contains all of foo's information, or checking that #foo contains .bar, .baz, .qux, and that each contains part of foo's information)
I've figured out some patterns and guidelines over time, but some of these tests tend to feel brittle or useless.