November 2015 Update: Greyhound.com changes their URLs - still ASP.NET, but very different GET-centric URLs whereas two years ago, this was POST-centric and I had to do a lot of hacking. TL;DR: don’t expect this project’s build to pass any more :(

I have been playing with a use of Angular that doesn’t follow the recommended Grunt/Bower/NPM best-practice tool-chain. The hypothesis is that many Java enterprises are not ready to go all the way down that road. It could be that developers are more comfortable with Java IDEs and build tools. It could be that the infrastructure people are not ready upgrade CI tools to be able to deal with Grunt and all that. I’m also trying to see if there’s a faster way of making Angular applications, having been part of teams that were faster with old (worse) technologies.

In this article, I outline a tier above greyhound.com’s booking service:

AngularJS with less JavaScript

Less JavaScript was one of the goals of the experiment. If we are still having to target Internet Explorer 7 & 8, then we need to worry about overwhelming those browsers. At least we need to regression test enthusiastically every week of a project that is supposed to support it. We also have to ensure that the open source we leverage supports it too. I’ve failed already in that regard by using AngularUI as it doesn’t support IE7. OK, whatever, lets press on with the article.

To meet the “less JavaScript” goal, I’ve tried to do less of the Angular-team recommended Services, Directives, and Modules. Instead I am sticking with a single JavaScript controller with a single list of injectable services. Don’t worry, I’m still doing separation of concerns at development time. I’m also avoiding Jasmine, which is perhaps the most controversial aspect of what I’m showcasing here. Lastly I’m not using Karma (or Protractor) for end to end testing. Instead I’ll be testing in Java, and using WebDriver (and some libraries) as mechanism to test-cover all the HTML, the JavaScript and a subset of the Java back-end itself. See Component Testing below.

I’m using TestNG, but JUnit would be just fine as an alternative to that. It could also as easily be .Net & NUnit or Ruby & RSpec too.

Bus tickets borrowing greyhound.com’s services

I’ve borrowed the “Web 1.5” app hosted on www.greyhound.com. It is currently written in .Net and uses commercial controls for some of the heavy lifting (Telerik). I’ve only delivered two components - Search Criteria, and Search Results. As this is totally unapproved usages of their services, you can’t actually buy tickets. I think if I went that far, I’d be getting a visit from the police. Greyhound could easily disable “support” for this proof of concept by checking the referrer for incoming service requests

Run-time technologies

These are not a strong recommendation, it’s just what I chose to support the idea of Component Testing in a browser for a hypothetical enterprise dev team. The exercise could have been as easily done in .Net or Ruby.

Server

  • Plain Java Servlets/Filters
  • Google’s HTTP client (needs a snappy name) - to interop with www.greyhound.com

Client

Build-time technologies

My dev machine’s setup

I’ve installed a DNS tool (dnsmasq) to my Mac. It allows me to map all of *.dev to 127.0.0.1. That allows me to open one page in Firefox (WebDriver) and have a different domain per test method, yet keep the same browser window open. I no longer have to worry about DOM state from one test leaking into another. I had this with ports changing in an earlier version, but figured domains was a better solution. Here’s the blog entry that speeded me through the setup for it. In the videos of the UI tests, you can see the domain name changing per test.

Other than that I’m using Jetbrains’ Intellij IDEA - the Rolls Royce of IDEs for 13 years.

Maven acquired dependencies:

Update: Moco is now using released artifact (v0.9), whereas I used to have a dependency on an unreleased version.

Selenium-WebDriver as a crutch

It was true for Selenium 1.0, and it is still a classic mistakes with WebDriver (Selenium 2.0):

  1. Going through a long path to get to the component under test (CUT) - specifically login and initial landing pages
  2. Being connected to the full stack for the sake of test, including known data starting positions, which up-time requirements
  3. Having brittle tests because or subtleties, inconsistent timings or #3 specifically
  4. Depending on Selenium to cover too much of your app, and therefore taking up too much of the total build duration

Dev teams always end up regretting too much Selenium usage.

Development

My Repo for this is https://github.com/paul-hammant/greyangular. It is quite buildable and runnable.

Component Pages

The basic goal was to have a working HTML page for each component only. Not only that, but you should be able to go directly to each, and not integrate with no other services from the “prod” codebase (at least during test automation). Indeed, testing and testability is the first design prerogative, but within the constraint of don’t write more JavaScript that you need.

You can try the component pages against real/live services like so:

mvn jetty:run

Search Criteria

Note the URL that’s just for this component, and the debugging in the page that Decdnorator will take it out as it stitches the final product.

Clicking “Search”, will go to the separate SearchResults.html component.

Search Results

Search Criteria params are to the right of the # in the URL. In the Java stack, the actual search is performed in association with this component.

Clicking “Show Schedule”, will go to the separate ShowSchedule.html component.

Show a Schedule

The Schedule for one search results route. A search must have happened first in order to be on the component.

Component Testing

Each of those three components has a TestNG test class. The test class tests positive, error, and exception paths for the component. The TestNG class instantiates WebDriver (in conjunction with FluentSelenium), and opens the page. The page goes off to the real CDNs for Angular, AngularUI and Bootstrap bits and pieces, but uses non-real services via Moco. In other words the tests will still pass if Greyhound.com is down (it was once during my development of this).

Here’s what I’m testing using a browser to cover all the JS + Angular-HTML paths that could go live:

  • Search Criteria Component
    • date and time interaction changes search model
    • clicking search button runs though validations
    • origin and destination interaction changes search model
  • Search Results Component
    • bogus params forces a return to criteria page
    • missing params forces a return to criteria page
    • no search results shows suitable error
    • progress bar appears before results
    • for search results clicking selection acts as radio button
  • Show Schedule Component
    • schedule can be shown
    • no schedule shows suitable error

The application being built & tested (Video)

Unit, Integration and Component (UI) tests

The whole test suite takes 33 seconds including all the Maven stuff. Five seconds of that is WebDriver opening Firefox (I wish there were a way to have the pre-opened). Anyway, 33 secs as I say:

mvn clean install

The Java unit tests take 5 seconds, but there is not that many of them. Similarly integration - 10 secs, and component (via Selenium/WebDriver) - 21 secs.

You could run those like so, if you wanted:

mvn clean install -PunitTests
mvn clean install -PintegrationTests
mvn clean install -PuiTests
# all three of those:
mvn clean install -PallTests

There’s no unit testing of the JavaScript via Jasmine (or alike). Instead the JavaScript, like the implicit logic in the Angular-enriched HTML, is tested via the Java + FluentSelenium tests. There is no coverage report for that, and I’m manually tracking the paths through the JavaScript codebase in order to be able to claim it is tested.

This style allows me to test the Angular “component” in one go. Classic Angular best practice would be to do Jasmine for separated out pure JavaScript functions, followed by Karma (up to now), or more likely Protractor (going forward). Why though (other than alleged Test-Driven-Development) do we have to separate that into two test phases. It means we’re more likely to try to make pure JavaScript functions so that we CAN test in Jasmine. It’s not like that’s a business goal. Sure Java+WebDriver is slower than Protractor (I’ve not figures), but not that much slower. If I can “cover” my turing complete HTML and JavaScript in one go, why wouldn’t I?

The final composed application (Video)

mvn clean jetty:run-war -Psites

I’m making two “final” sites. One that uses the accordion control from the Angular-UI Bootstrap library (search results below search criteria), and one that uses a tab-set (search results to the right of search criteria). Because this is only a proof of concept, I’ve called one index_1.html and the other index_1.html, but they are totally separate.

You would deploy these two as index.html to two different domains (and CDN at that), and link them back to the same servlet instance. Or you’d use Google’s AppEngine to host one each, including the servlet app (Greyhound.com is your REAL backend in this case). I like to think as AppEngine as a CDN with a built-in servlet container.

The two brands:

Accordion (49 secs, incl build)

Play with AngularUI’s accordions here http://angular-ui.github.io/bootstrap (10% of the way down the page)

Tabset (24 secs)

Play with AngularUI’s tabsets here http://angular-ui.github.io/bootstrap (80% of the way down the page)

Full-Stack (UI) tests

Yet more tests with the real Servlet container (no Moco), and hence real Greyhound.com services. That introduces brittleness of course. The upside is that I’ve reused the component tests code. Those two brands via WebDriver tests:

mvn clean install -Psites,fullStackTests

More about the tech solution

A different type of modularity

As mentioned in passing before, SearchCriteria.html and SearchResults.html are mostly self-contained. Well apart from the need for backend services. Its JavaScript and HTML are all in one source file. It’s pretty much useless in the context of a production deployment, but at dev/test time it is ‘right sized’ for interactive experimentation and automated QA (via WebDriver).

For final sites, Decdnorator will parse that source, and remove the pertinent HTML and JavaScript fragments. Assuming all automated tests pass, that is. Decdnorator in this case also inserted the “Show Schedule” component into the “Search Results” one. It could have been a different affordance even, like an overlay modal, just as easily. Angular’s recommended ‘Grunt’ toolchain has a htmlbuild module that has a similar capability.

The take away is that the HTML component pages are designed for testability. Even the attaching of CSS is unnecessary at that stage.

Throwing the baby out with the bathwater?

An expression for those not native in English.

Many will think I have gone too far with this. Here is what I could have done with a more idiomatic solution:

ngView and ngInclude

These are dev team recommended way of modularizing Angular HTML fragments. They allow such HTML code to be shared into multiple locations in an app, and separated for coding/testing purposes (Jasmine and Karma normally). I’m using Decdnorator as a poor approximation of this. My downside is that HTML fragments may be shipped to the browser more than once, though many would claim its not going to be a problem if things are inline and cashable.

In my app, I made two “single page apps” (SPAs) for two different brands. A third brand could be more like the page-separated components that were tested with WebDriver. They’d be smaller, and it would be possible to retain all the state management and back/forward button processing we want (yes yes, mine has bugs in that regard at time of publication).

Angular’s $routes and $location

As well as mapping http://example.com/resource/#/routeXyz to separate views, Angular’s $routes & $location services allow for the URL to change without the page changing. At least much more smoothly that I have it now. This should be amongst the first things a team would put back in versus the regrettable usage of window.location.href as it is now. If nothing else, a more sophisticated application is going to need such things sooner or later.

Services, Directives and Modules per se

Classic Angular apps have server interoperation hived off into services. I’m “just” using the controller for everything. There is no real difference in the runtime aspects of either, it’s only a developer consideration. Cleanliness / separations would be the prerogative for that. Angular has some dependency Injection style capability for making sure the JavaScript functionality is hooked up appropriately at run time. In my solution I’m avoiding that too (despite being one of the pioneers of Dependency Injection) because I don’t need it.

Scopes, $apply and $watch

Because I’m stitching a single controller, I only really have one scope. Actually HTML form usage makes another small scope (and I’d rather it did not). I’m not using $apply anywhere, and I’m not using $watch on any model object. More about these in another blog entry. Of course Angular-UI could be using both of those (and nested scopes) under the hood, but that is none of my concern.

Jasmine, Karma or Protractor

These are from the Angular team. Karma used to be called Testacular, and the team had huge critical acclaim with JsTestDriver before that. I’m not using any of these. I expect the greatest amount of criticism for the implicit proposal I’m making from that choice.

As Agile enthusiasts we’ve been keen on Test Driven Development (TDD) for the longest time. And from that, with good mocking techniques, we claim to be able to move fast, refactor casually, and arrive and “most stable” for the application soonest, and stay there as we expand functionality over a series of iterations. Then there is the “Test Pyramid” (Martin’s Article) that suggests we should have a lot of unit tests, less integration tests and less still functional tests. Without all that good agile balance, we’re heading for trouble, especially so if we’re too focussed on design up front (or have no design in mind at all) (Martin’s ‘Design Stamina Hypothesis’ Article).

I don’t think there is no design, or too much design up front here. Really, all I set out with was 1. Component Pages, 2. Component tests with Java/Selenium, and 3) stitch bits together for a finished site. I knew I’d have to get inventive so that components cooperated on one bus.

Jasmine is the one that’s notably missing. Java, FluentSelenium, ngWebDriver and WebDriver are not a replacement. Jasmine is for pure JavaScript functions though. I only have one locnInError(..), and it is four lines. It gets coverage via the WebDriver component tests, even if that coverage is second class. Sure, many more of my JavaScript functions could be split into two - one pure JavaScript and one dependent on the AngularJS environment in the browser. That seems gratuitous to me.

Error indicators

Angular normally uses $error property scotch-taped to model objects. I don’t really like it so I’m not using it. Instead I have another item rooted in $scope called errors. I have to manually reset on each revalidation.

Yet more about the development experience

Moco

Moco was fun to use, but I wish it were a little more like Mockito. One thing that’s probably insurmountable is the fact that the server (Moco’s HTTP handler) and the client (Java invocation of WebDriver via FluentSelenium) are on different threads, while under TestNG control. It would be the same for JUnit. The issue is that you want to fail on the server side, if a parameter came back that was unexpected. You can’t do a assertion error in the thread that’s responding to incoming HTTP threads, as it is not going to get reported back to TestNG as a failure - it’s going to get consumed, or at best passed to System.err. Thus, I’m doing a StringBuilder trick to capture params, then verify them back in the main thread (that’s under TestNG’s control).

TestNG and Guice injected things

TestNG (a first time for me) is interesting. JUnit has always felt static, and I’ve a problem with that generally given a history with Inversion of Control and Dependency Injection going back 14 and 10 years respectively. TestNG is more instance centric in terms of the runner, and allows advanced things like Dependency Injection. I’m using that to pass in a single Window of Firefox under WebDriver/FluentSelenium control. All test methods share that - yay! The trouble is that I found myself using a ModuleFactory (a Guice-TestNG thing), and the runner instantiated that N times. Yeesh - I only wanted one Guice Module making one WebDriver for all tests. So, I’m keeping the Module as a static variable and making it is only made once with a shitty guard:

Thanks

  • Kate Moczydlowski for helping experiment with Moco and borrowing Greyhound.com services.
  • Brandon Byers for a proof read and comments - particularly re Jasmine and design for testability.


Published

October 23rd, 2013
Reads:

Tags

Categories