Running some trials with protractor to prove I can mock data per test case
If you’ve been a Web Driver user long enough, you know that you can emit arbitrary machinery
at runtime. With this hook, the possibilities are limitless. This is what I like to call “the
portal”. It’s like this hole in your browser that allows custom JavaScript to make it’s way
through...at will. kbaltrinic/http-backend-proxy
is leveraging this feature of Web Driver and
as a result....well....we’re Gucci.
When I first started to consume kbaltrinic/http-backend-proxy
, my head exploded. I saw stray
references to $httpBackend
, and I wondered where they came from. Did his API provided them?
Did protractor provide it? This all being in the context of node as that’s where your tests
drive the UI from. I would type out what I saw verbatim on the screen, and bang...reference error.
I’m like hwa? How can this be? I scanned his doc over and over again, and my epiphany was born. Remember
the first section where I reference “the portal”? http-backend-proxy
takes your callbacks, serializes
them, and publishes them to the browser’s context (as if it was originally included in your app all along).
Kool, weird, and kinda freaky. If this were .NET, we’d be screwed. csc (the compiler) would start doing it’s thing and
yell at us about some type or variable not being defined. However, “this is JavaScript...man”. The neat
thing about callbacks is that they aren’t executed until....they’re executed. Some code somewhere
has to invoked your callback, or it’ll never run. It’s kinda scary if you think about it. Because
if a callback contained a bug, you’d never see it unless the callback were triggered (chances are...it would be).
Let me give you an example to illustrate my point:
function test(fn) { fn() }
test(function() { cons.long('hi there'); })
ReferenceError: cons is not defined
See that? I purposely misspelled console and it bombed out. Watch what happens when I update test
to not invoke
it’s callback:
function test(fn) { console.log('not invoking callback. just koolin.') }
test(function() { cons.long('hi there'); })
undefined
"not invoking callback. just koolin."
See? No reference error there. Firefox’s console spits out the return value of the function (undefined
), and
logs some text to the console. But all clear (no erors). This is how code like this works
If you take a look there, you’ll see that $httpBackend
is being referenced in the callback. It isn’t defined anywhere though,
not even globally. http-backend-proxy
will serialize that guy, and by the time it’s run in the browser, your app will be expected
to have loaded $httpBackend
via ngMockE2E
. That’s how this all works...man. http-backend-proxy
is basically just a library that
emits code based on angular and $httpBackend
. It makes it look as if that code were in your app the entire time (dynamically spits it out).
Notice the word “serialize”. Your callback isn’t being invoked directly, but rather converted into a string and sent off to Web Driver.
I can’t underscore this enough. Once this settles into the pores of your memory banks, life gets that much easier.
ngMockE2E
and angular-mocks
are required for all this magic to work. That means somewhere you’ll have to write code like
this.
You build process would also have to inject angular-mocks
into the page. Why is all this necessary? Because that’s where
$httpBackend resides
...inside angular-mocks
. Simple enough right? Well...the problem is...if your app just blindly relies
upon ngMockE2E
it’s going to fail under general usage (when you’re not running tests in the context of protractor). This
is because $httpBackend
expects requests for each url to be configured. When I load my app outside protractor, I get something
like:
"Error: Unexpected request: GET https://api.github.com/users/armw4
No more request expected
$httpBackend@http://localhost:3000/angular-mocks/angular-mocks.js:1226:1
sendReq@http://localhost:3000/angular/angular.js:9619:1
$http/serverRequest@http://localhost:3000/angular/angular.js:9335:16
Hence the birth of #1. Now..you could have your app just kinda
hack $httpBackend
and do a pass through for all requests when your’re not running protractor, but I would not recommend
that. I would say go all or nothing. Exclude ngMockE2E
and angular-mocks
all together when not running e2e tests. However
you decide to skin this cat is of course up to you. I’m just the messenger...you know the drill.
I’ve already written about the use of page objects and why I believe in them. So no need to beat that horse, it died already. I just wanted to make it clear that they’re being used here. If I’m gonna do this fulls scale (as is the plan for our internal application), I’d want to make use of two basic constructs:
And these guys:
These guys will dry things up quite a bit and encapsulate a lot of boilerplate. This, combined with the aesthetically pleasing and Ruby-esque syntax of Coffee Script make for one hell of a show. It puts the fun back in e2E for angular apps in my mind.
I spent hours trying to figure out why my specs were failing. Then I saw this.
There’s the concept of "on load" configurations. Basically if when you initially load your pages, and requests are triggered without
any user interaction (bootstrapping for example), then you will want to register these guys for onLoad like so.
The first thing that happens when my page loads is a request to the github API. If I were to remove that call to @proxy.onLoad
,
my specs would fail. Everything else will pretty much be a barebones configuration. You’d just skip the call to onLoad
like
this guy.
Most requests to a remote server will require some sort of user interaction (i.e. button click) before they’re initiated. That’s
what I consider to be the “Everything Else” category.
This may seem like a no brainer, but don’t try looking up elements prior to the DOM being loaded (some call to brower.get
).
You’re gonna get element not found exceptions and crap like that. Also make sure any onLoad
mock configurations get set up
before the page is loaded. It goes:
”And in that order...”
Well...I think that’s pretty much all you need to know in order to get up and running with this bad boy. I would recommend heading
over to the kbaltrinic/http-backend-proxy
README for more details. It’s there that you’ll find out the usage and importance of the
context
object for example.
I think this project is really kool and it’s gonna serve as another one of my swiss army knives. As always...
”Roll em’...”
🍟 ☕ 🏉 📆 💶 📺 🎒 🎍 🌝 🐗 💪 💦 💥 😂 😜 🐊 💐 🐃 🐣 🐎 🐢 🎋 🍟 ☕ 🏉 📆 💶 📺 🎒 🎍 🌝 🐗 💪 💦 💥 😂 😜 🐊 💐 🐃 🐣 🐎