Showing posts with label angularjs. Show all posts
Showing posts with label angularjs. Show all posts

Saturday, February 8, 2014

Mocking Promise Objects within AngularJS

Assumption: You understand the value of breaking up your application into modules.  Too many: one module per service/controller, Too few: one module for entire application, assuming your application is not a basic TODO. Long story short: with modules, you can reduce the scope of your test without having to include the full application with each test.

Background: We’ve been using AngularJS to help with our new order entry system and it’s working great.  Testability is at the forefront of angular’s offering and we attempt to have as much valuable code coverage as we can within our application.  Early on, we encountered an obstacle while trying to test $q within a controller’s init function.

Obstacle:  One of our controllers has to init with a promise object and several of the scope objects within the controller depend on the resolution of the promise.

Controller to test


  • you can see we have a client module
  • product module is an external dependency, we don’t want to bring that module into our test
  • the product service returns a promise object, most likely $q since we’re using AngularJS

Test with mocked promise object



  • when we first encountered this problem, we were forgetting we were JavaScript developers…it’s easy to get caught up in the JS library itself and expect the library to solve all of your problems for you…can we use $httpBackend here? no…what if your promise isn’t really even working with http?  what if it is but you don’t want to test the http call since that (module) is out of scope for this test?…why doesn’t ng do something for us - who cares…JS is easy enough without ng!
  • since JS is one of the easiest languages to mock (if not the easiest), just create an object in your beforeEach to resemble the promise you require
  • you can see we mock the entire productService & testing the productService itself should be out of scope here
  • our test is really simple but it proves the concept for mocking promises

Thursday, February 6, 2014

AngularJS Testing: Consolidating Injections

ASSUMPTION: You are familiar with the following frameworks

Angularjs
Jasmine
Nodejs
Karma
Grunt

With AngularJS, you get Dependency Injection - duh, great post so far…

You love DI so you start cranking out tests injecting and mocking everything under the sun.  You wake up 3 months later and you have a nasty code smell: Copy Paste Monsters are everywhere

Solution: Try your best not to be so damn lazy…I fight it every 2 seconds…

Not Ideal


  • you can see this spec really contains 2 simple tests, each injecting dependencies into the test ($rootScope, $controller, clientOrderModel)
  • the controller probably displays contact information and validation is needed for the contact email (we’re just checking if it’s present, not real email validation: have a heart)
  • you created the first test and went through your normal: red -> green -> refactor…you refactored the controller/service/thingy but forgot to refactor the test…who cares, it’s green - move on already…then time for test two - reach for your friend the copy paste monster and you’re flying high
  • at first glance this might not seem like a big deal but over the span of 3 months, each couple of sprints adding new developers who themselves want to copy/paste from the previous guy because their too afraid to mess something up…and BAM: you have way too much code everywhere and you forgot to stay DRY
  • tests are harder to maintain, read, etc. - same old story presented a million times by testing authors over the last 15 years
Better


  • you can see we’ve moved each of the inject statements into 1 beforeEach, we’re also able to do this because each test requires the same dependencies
  • you can also see each of the tests is much easier to read, really only a couple of lines
  • a nice caveat to this approach: folks coming in after you will be more willing to write tests too, especially if they’re less complex, easier to write & understand, blah…
Special Note

No, you haven’t won anything for actually reading this BUT pay close attention to the customerOrder object being used in each of the tests. Please remember to always reset your mocked objects in a beforeEach clause.

Why?

Because objects are reference types, each change you make to the object will affect other tests - assuming you are referencing the same object in multiple tests: BAD.  Using the beforeEach clause will ensure each test starts out pristine.