Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Feed Your Grails Karma (#ggx 2014)

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Chargement dans…3
×

Consultez-les par la suite

1 sur 70 Publicité

Feed Your Grails Karma (#ggx 2014)

Télécharger pour lire hors ligne

If you want to step out of the comfort zone of the scaffolded Grails Server Pages and build a Single Page Applications in Grails, there are a variety of tools in the JavaScript ecosystem that give you better support than the more traditional and well known back-end Groovy solutions. Adding a combination of the Grails Asset Pipeline and Bower gives you Maven like dependency management. Karma and Jasmine will do for testing instead of Spock. You only need to ensure that your back-end and front-end are kept in-sync.

In this session I'm sharing the lessons learned while developing the Model Catalogue Grails Plugin for managing metadata. You will see how to setup test pipeline on your continuous integration server to generate JavaScript fixtures from your controller Spock specifications which will then be used for testing AngularJS services and directives with Jasmine and Karma.

You can watch the video online at https://skillsmatter.com/skillscasts/6075-feed-your-grails-karma

If you want to step out of the comfort zone of the scaffolded Grails Server Pages and build a Single Page Applications in Grails, there are a variety of tools in the JavaScript ecosystem that give you better support than the more traditional and well known back-end Groovy solutions. Adding a combination of the Grails Asset Pipeline and Bower gives you Maven like dependency management. Karma and Jasmine will do for testing instead of Spock. You only need to ensure that your back-end and front-end are kept in-sync.

In this session I'm sharing the lessons learned while developing the Model Catalogue Grails Plugin for managing metadata. You will see how to setup test pipeline on your continuous integration server to generate JavaScript fixtures from your controller Spock specifications which will then be used for testing AngularJS services and directives with Jasmine and Karma.

You can watch the video online at https://skillsmatter.com/skillscasts/6075-feed-your-grails-karma

Publicité
Publicité

Plus De Contenu Connexe

Plus récents (20)

Publicité

Feed Your Grails Karma (#ggx 2014)

  1. 1. Feed Your Grails Karma Vladimír Oraný
  2. 2. Overview Test Driven Backend REST Application (Grails, Spock) Test Driven Frontend Single Page Application (AngularJS, Jasmine, Karma, Geb) Continuous Integration capable Test Pipeline (Travis CI)
  3. 3. Grails Rapid web development framework Plays well with Test Driven Development … … as long as you stick with GSPs Great REST support since 2.3
  4. 4. Geb Browser automation solution jQuery-like selectors plays well with Grails and Spock great tool for functional tests
  5. 5. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  6. 6. Model Catalogue Plugin Grails PLugin for managing any metadata REST API Backend AngularJS services and directives https://github.com/MetadataRegistry/ ModelCataloguePlugin
  7. 7. Single Page Application “A single-page application (SPA), is a web application or web site that fits on a single web page with the goal of providing a more fluid user experience akin to a desktop application.” –The Poeple of the Internet (aka Wikipedia)
  8. 8. Ancient Architecture Server Fat Client Shared Libararies
  9. 9. SPA Architecture REST backend Rich Client SPA REST API (JSON)
  10. 10. Sample Application(s)
  11. 11. Earl’s List Backend application in Grails Frontend application in AngularJS with little help of Grails (Asset Pipeline) https://github.com/musketyr/earls-list
  12. 12. Backend Application Grails REST application grails create-app earls-list cd earls-list grails create-domain-class org.example.todo.Item grails create-controller org.example.todo.Item
  13. 13. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  14. 14. @Unroll void “item from #params has #errorCount errors"() { when: Item item = new Item(params).save(failOnError: true) then: item.errors.errorCount == errorCount where: errorCount | params 1 | [description: ''] 0 | [description: 'Do IT'] 1 | [description: 'x' * 256] }
  15. 15. class Item { String description Boolean crossed = Boolean.FALSE static constraints = { description size: 1..255 } }
  16. 16. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  17. 17. class ItemControllerSpec extends IntegrationSpec { void "create new item"() { when: controller.response.format = 'json' controller.request.json =[description: 'Do IT!'] controller.request.method = 'POST' controller.save() def result = controller.response.json then: result.description == 'Do IT' }
  18. 18. class ItemController extends RestfulController<Item> { def responseFormats = ['json'] ItemController() { super(Item) } }
  19. 19. @TestFor(UrlMappings) @Mock(ItemController) class UrlMappingSpec extends Specification { void "test item rest endpoints ready"() { expect: assertRestForwardUrlMapping(method, url, controller: "item", action: action, paramsToCheck) where: method | action | url | paramsToCheck "GET" | "index" | "/item/" | {} "POST" | "save" | "/item/" | {} "PUT" | "update" | "/item/1" | { id = "1" } "DELETE" | "delete" | "/item/1" | { id = "1" } }
  20. 20. class UrlMappings { static mappings = { "/item/" controller: 'item', action: 'index', method: HttpMethod.GET "/item/" controller: 'item', action: 'save', method: HttpMethod.POST "/item/$id" controller: 'item', action: 'update', method: HttpMethod.PUT "/item/$id" controller: 'item', action: ‘delete', method: HttpMethod.DELETE } }
  21. 21. Frontend Application AngularJS for controllers, services and templates dependency injection => easier testing Bootstrap for theming Jasmine and Karma for testing
  22. 22. SPA Architecture REST backend Rich Client REST API (JSON)
  23. 23. JsonRecorder Records the JSON i/o in the controller tests repositories { mavenRepo "http://dl.bintray.com/metadata/model-catalogue" } dependencies { test "org.modelcatalogue:json-recorder:0.1.0" }
  24. 24. JsonRecorder def rec = JsonRecorder.create('test/js-fixtures/earlslist', 'item') void "obtain the list of items in json format"() { when: controller.index(10) def result = rec << 'list' << controller.response.json then: result?.size() == 10 result[0].description =~ /Item #d+/ }
  25. 25. (function (window) { window['fixtures'] = window['fixtures'] || {}; var fixtures = window['fixtures']; fixtures['item'] = fixtures['item'] || {}; var item = fixtures['item']; window.fixtures.item.list = [ { "id": 17, "description": "Item #16", "class": "org.example.todo.Item", "crossed": false, "dateCreated": "2014-12-07T04:56:44Z" }, ... ]; })(window);
  26. 26. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  27. 27. Where to find frontend tools and libraries?
  28. 28. This slide was intentionally left blank …
  29. 29. Front-end Developer Toolbox
  30. 30. NodeJS Platform for creating server application in JavaScript Platform for creating/sharing tools for frontend developers Package manager NPM npm install Plays well with CI servers
  31. 31. package.json { "name": "earls-list", "description": "Sample TODO List", "version": "0.0.1", "devDependencies": { "bower": "~1.3.12", "karma": "~0.12.0", "karma-junit-reporter": "~0.2.1", "karma-jasmine": "~0.2.0", "karma-firefox-launcher": "~0.1.3" } }
  32. 32. Bower Package manager for frontend application Based on Git bower.json with similar syntax as package.json (NPM) bower install
  33. 33. bower.json { "name": "earls-list", "description": "Sample TODO List", "version": "0.0.1", "dependencies": { "angular": "~1.3.5", "bootstrap": "~3.3.1" }, "devDependencies": { "jasmine": "~2.1.3", "angular-mocks": "~1.3.5" } }
  34. 34. .bowerrc { "directory": "grails-app/assets/bower_components" }
  35. 35. Asset Pipeline Plugin //= require jquery/dist/jquery //= require angular/angular //= require bootstrap/dist/js/bootstrap //= require items //= require itemsCtrl angular.module('earlslist', [ 'earlslist.items', 'earlslist.itemsCtrl' ]);
  36. 36. Asset Pipeline Plugin <!DOCTYPE html> <html> <head> <title>Earl's List</title> <asset:stylesheet src="earlslist/items.css"/> <asset:javascript src="earlslist/index.js"/> <script type="text/javascript"> angular.module('earlslist.apiRoot', []) .value('apiRoot', '${request.contextPath ?: ''}'); </script> </head>
  37. 37. Jasmine Behaviour-Driven JavaScript Assertions with matchers Mocking and Spying
  38. 38. describe('Items service wraps the backend', function(){ // setup beforeEach(module('earlslist.items')); it('should list all items', inject(function(…){ // feature method })); ... }
  39. 39. it('should list all items', inject(function(items, $httpBackend){ var list = null; $httpBackend .expectGET(‘/item/‘) .respond(angular.copy(fixtures.item.list)); items.list().then(function(fetched){ list = fetched; }); expect(list).toBeNull(); $httpBackend.flush(); expect(list).toBeDefined(); expect(list.length).toBe(10); }));
  40. 40. it('should create new item', inject(function(items, $httpBackend){ var item = null; $httpBackend .expectPOST('/item/', fixtures.item.validSaveInput) .respond(angular.copy(fixtures.item.validSave)); items.save(fixtures.item.validSaveInput).then(function(created){ item = created; }); expect(item).toBeNull(); $httpBackend.flush(); expect(item).toBeDefined(); expect(item.id).toBe(35); }));
  41. 41. angular.module(‘earlslist.items', function($http, apiRoot){ var items, unwrapOrReject, enhanceAll, enhanceItem; unwrapOrReject = function(response){…}; enhanceItem = function(item) {…}; enhanceAll = function(listOfItems) {…}; return { list: function(start) { return $http({ url: apiRoot + "/item/", method: 'GET', params: start ? {offset: start } : {} }).then(unwrapOrReject).then(enhanceAll); }, ... }; });
  42. 42. Karma Test runner for JavaScript tests Can run multiple browsers including HtmlUnit and PhantomJS karma start karma.conf.json
  43. 43. module.exports = function(config) { config.set({ ... files: [ 'grails-app/assets/bower_components/jquery/dist/jquery.js', 'grails-app/assets/bower_components/angular/angular.js', 'grails-app/assets/javascripts/**/*.js', 'grails-app/assets/bower_components/angular-mocks/angular-mocks.js', 'test/js-fixtures/**/*.js', 'test/js/**/*.js' ], ... }); };
  44. 44. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  45. 45. <body ng-app="earlslist"> <div class="container"> <div ng-include="'earlslist/items.html'"></div> </div> </body>
  46. 46. ctrModule.run([‘$templateCache', function($templateCache){ $templateCache.put('earlslist/items.html', '<div class="row" ng-controller="earlslist.itemsCtrl">' + ... '</div>'); }]);
  47. 47. <form ng-submit="addItem()" class="col-md-12 form"> <div class="alert alert-danger" ng-repeat="error in errors"> {{error.message}} </div> <input ng-model="newItemText" ng-disabled="loading"> </form> <h2 ng-repeat="item in items"> <span ng-click="toggle(item)"></span> <span class=“item-description" ng-click=“toggle(item)”> {{item.description}} </span> <span class="pull-right" ng-click="remove(item)">Delete</span> </h2>
  48. 48. angular.module('earlslist.itemsCtrl', ['earlslist.items']) .controller('earlslist.itemsCtrl', function($scope, items){ ... $scope.addItem = function() { items.save({description: $scope.newItemText}) .then(function(newItem){ $scope.newItemText = ''; $scope.items.unshift(newItem); $scope.errors = []; }).catch(function(response){ $scope.errors = response.data.errors; }); }; });
  49. 49. beforeEach(inject(function ($rootScope, $controller, _items_, $q) { items = _items_; $scope = $rootScope.$new(); // returns 10 items for first call and 3 for second spyOn(items, 'list').and.callFake(function () {…}); $controller('earlslist.itemsCtrl', { $scope: $scope, items: items }); }));
  50. 50. it('should list all items', function(){ expect($scope.errors.length).toBe(0); expect($scope.items.length).toBe(0); expect($scope.loading).toBe(true); $scope.$digest(); expect($scope.errors.length).toBe(0); expect($scope.items.length).toBe(13); expect($scope.loading).toBe(false); });
  51. 51. it('renders 13 items', inject(function($templateCache, $compile) { var tpl, element; tpl = $compile($templateCache.get(‘earlslist/items.html')) element = tpl($scope); $scope.$digest(); expect(element.find('.item').length).toBe(13); }));
  52. 52. GSP vs. SPA DB Services View Support Views Domains Domains GSP SPA Serv + Ctrl S + C + REST Taglibs Components GSP Not Covered App Func Func Covered Templates Legend
  53. 53. Functional tests with Geb reportsDir = new File("target/geb-reports") reportOnTestFailureOnly = false baseUrl = 'http://localhost:8080/earls-list/' driver = { new FirefoxDriver() } waiting { timeout = 15 retryInterval = 0.6 } // Default to wraping `at SomePage` declarations in `waitFor` closures atCheckWaiting = true
  54. 54. class HomePage extends Page { static url = "#/" static at = { heading.text() == "Earl's List" } static content = { heading { $("h1") } task { $("#task") } items(required: false) { $(".item") } itemsTexts(required: false) { items.find('.item-description') } errors(required: false) { $(".alert-danger") } closeError(required: false) { $(".close") } } }
  55. 55. def "go to home page and enter new task"() { when: to HomePage then: at HomePage expect: !items.size() when: task = 'Test with Geb' task << Keys.ENTER then: waitFor { items.size() == 1 } }
  56. 56. „Will it play on continuous integration server?“
  57. 57. Travis CI free for public GitHub repostories configuration stored in the repository NodeJS available for every build Firefox and Chrome for headless testing
  58. 58. .travis.yml language: groovy jdk: - oraclejdk7 before_install: - "npm install" - "bower install" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" script: - "./grailsw refresh-dependencies" - "./grailsw test-app" - "./node_modules/karma/bin/karma start --single-run --browsers Firefox"
  59. 59. Summary Grails SPAs are actually two applications Use the front-end developers tools for front-end app Use your backend tests to generate the fixtures https://github.com/musketyr/earls-list
  60. 60. Thank you Vladimír Oraný @musketyr

Notes de l'éditeur

  • Hello, I would like to welcome you to my talk Feed Your Grails Karma. My name is Vladimír Oraný and first of all I have to confess to you: I'm a test driven development addict. I like that soft and warm feeling when I have my code covered by tests and when I change something I see the warning sign immediately: Hey man, do you know that you've just broken this part of application with your brand new feature?
  • I'm pretty sure I suffered by this addiction for a very long time. Everything was fine as long as I was developing some library or a backend code. Over the years I've found a lot of brilliant tools such as Spock Framework which helped me satisfy the need of having code covered by tests. On the other hand, I've strongly rejected to work with libraries which don't support TDD. At the end, first question I always ask myself when I am adopting new tool is - Will it play well on integration server?
  • That's the reason why I love Grails. Grails has such a great support for TDD and CI as well. As long as you stick with GSPs. If you don't care about your users' experience and you're fine with default scaffolded views and all that click-n-load-everything-again approach from last or last but one decade you have no problem. If you do care about your users and you want to create the application as fast and friendly as possible you start reaching the boundaries. You do have a problem. It's not about AJAX anymore and g:remote tags, it's about REST APIs and rich single page applications consuming the REST services.
  • You may ask: What about functional tests? What about Geb? Geb is great tool, functional tests are useful. Write them! Actually write them in any tool you like. It doesn't have to be Geb. But these are just black-box tests and we are Groovy developers. We totally ignore objects privacy to test the last bits of our library or backend code. And now we should be happy with just black-box testing when we're so used to all these x-ray glass-box tests? [Click] We just want to see what’s inside that black box.
  • There is a huge gap between what can be tested with standard Grails tools and what I would like to test.

    And now imagine how does it look if you don't have any user interface at all because you're developing just a frontend library for other application as I did for a last year.
  • We've created a Grails plugin which provides REST API to other Grails applications and we've provided JavaScript libraries to incorporate the REST API to other Grails applications. So we definitely wanted to cover those grey areas we’ve seen before.

    But wouldn’t be probably that crazy to develop similar plugin so I’ll rather show you simpler example of creating a Single Page Application.
  • I've seen a couple of presentations which explained how to write SPA with Grails but can't remember one which really emphasized the A in SPA. Single Page Application is an application. And I thing it’s much clearer if you thing about the SPA as is a completely different application than your backend application. Because, you know, you can have SPA without any backend storing the data to browser’s local storage. And you can have REST backend just providing services for other backends.
  • Once we had this wisdom of separation the client and server but many of us already lost it. It was in late 2006 when I earned my first money by developing software. It was server application with fat client written on top of Eclipse Rich Client Platform. We used all the cool standards Java was giving us such as Java Remote Invocation. Eclipse was our build tool. Tests were a swear-word but we were quite happy be didn't know how to plug them to that Eclipse build anyway. We've always kept in mind that the server and the client are two separate things. Both had their own lifecycles, assigned resources and so on. But on the other hand we know they have to share something to work together. For this particular situation it was that remote invocation code.
  • But yet when we’re developing Single Page application we tend to handle it as one single application. But that’s not true. We still basically have two very separate application which communicates with the REST JSON API.
  • Realizing that we are basically developing two separate applications is just an initial step to success. Let’s take a deeper look with the sample application or I should rather say sample applications.
  • It’s always great to start developing an application with the user. His name is Earl. He’s a petty criminal and good-for-nothing who already knows something about Karma. He had a $100000 lottery ticket but got hit by car and lost it. In hospital, heavy drugged by morphine, listening to some lousy TV show talking about Karma principles - do bad things and bad things happens to you and vice versa he wants to write a list of all bad things he ever did to fix them. When he crossed the first item he found the ticket back so he have some money to spent to create new application for keeping his list online.
    http://blog.whitepages.com/wp-content/uploads/2014/10/My-Name-is-Earl.png
  • So let’s help Earl with his list. I will only show few code samples but the complete code is available online so you can use it for an reference or as an template. There is Gitter enabled, which is basically an IRC chat so you will be simply able to ask questions about this application later easily.
  • Creating and REST Grails application is extremely easy thanks to new REST support added in the latest versions. I’ve put here a short list of commands just for later use if you want to try it yourself.
  • It’s very easy to cover the domain classes with tests thanks to great Grails support.
  • This is how can a sample specification for a domain class look like.
  • And here’s that very simple domain class for our TODO items. You can see I’ve constrained the string property size as if you don’t to so it will use to database defaults anyway throwing the runtime errors.
  • Also testing controllers and services is pretty straightforward in Grails
  • You just need to pay attention to couple of things when you’re writing specification for REST backend.
    Even there is support in Grails for unit testing the controllers you can’t really use it for testing REST controllers as they won’t work. You need to use integration specs instead.

    You should always specify the response format and the request method.
    You should test that everything the frontend app will rely on is present in the returned JSON.
  • The controller itself is extremely simple. As we’re not introducing any new logic, we’re just fine with the defaults. We only want to support json so we can simpify the getObjectToBind method to only return the JSON from the request.
  • As REST API is defined as combination of URLs and HTTP methods we should never forget to test the url mappings as well. Sadly there is no built in support for testing RESTfull mappings. Please feel free to copy paste he assertRestForwardUrlMapping method from the demo app.
  • The final mapping satisfying the previous test is pretty streightforward.
  • http://img4.wikia.nocookie.net/__cb20081028212204/mynameisearl/images/d/db/2x03a.jpg
  • The reason to choose AngularJS were basically three:
    it supports dependency injection, that mean
    it's easily testable as you can mock any services easily
    I had no clue how will AngularJS 2.0 look like (it will be completely different thing).

    No need to explain why I've chosen Bootstrap for these who seen how did the home brew UI made by backend developers without any artistic sense looked like before (Reddit)
  • Before we start coding the front-end application we need to stop and thing a bit. But how to grasp the JSON REST interface between the server and rich client application. Well, we’re already testing the JSON input and output in the controller integration spec.
  • So we only need to record it for later use.
    I’ve created just a minimal helper which is currently published on our BinTray account.
  • There’s nothing complicated with the recorder. You point it to the directory you want to store the test fixtures and give it a name of the domain class - this will be the container object in the fixtures which will contain for example the list object with the JSON list result as we can see in the next slide.
  • Each of the fixtures file generated will try to create the container object if it does not exist and assign the value with the JSON input or result.

    You can see there is a fixtures.item.list object present when the this fixtures file is executed. With that we can proceed to testing the view support such as services in AngularJS.
  • Well when we have our API to test agains we can proceed to the tests themselves. We would like to create a AngularJS service which will create abstraction atop of the http calls to the backend and so let’s create tests for it.
  • As I noted in the beginning I want the application to be CI enabled so I want to use some package manager to get the front-end dependencies.
    As a Grails developer your first steps might go to Grails Plugin Central.
  • Well you can find an AngularJS plugin there
  • As well as plugin for the Bootstrap
  • Even Jasmine is there.
  • And even a test runner for Karma for Grails. Stunning!
    The good part about AngularJS is that there is already huge ecosystem created around this framework. People are writing modules to plug third party frameworks into AngularJS what if someone already written for example module which plugs Bootstrap JavaScript into AngularJS.
  • There is for example angular module for bootstrap for the JavaScript part of the library.
  • There’s no Grails Plugin for this. We’ve reached the boundaries what we can do with Grails provided dependency support.
  • To develop front-end application effectively we have to get rid of our backend developers’ habits and start using the frontend developer tools otherwise we reach the boundaries very soon.
  • Some of you may look at NodeJS as a naive tool for writing server application in JavaScript but it become something else. A toolbox where frontend developers can find and share brilliant tools. It comes with a package manager called NPM and

    And the best part is that it plays well with major CI servers.
  • The demo application is pretty slim so we only need something called Bower and Karma with few of its dependencies.
  • Instead of trying to create Grails resources plugin for everything we can use Bower. It was created by same developers who built Bootstrap in 2012. It has very similar syntax to NodeJS package.json but it focuses on providing dependencies for your web pages instead of your web server. The libraries are basically just pure Git repositories where each version is defined by git tag which makes it very easy to contribute new stuff.
  • Our bower.json file contains just AngularJS and Bootstrap for the single page application and Jasmine and Angular Mocks for testing.
  • As I’ve said before we want to benefit from Grails Asset Pipeline plugin while developing the Single page application so we want to redirect the directory where the downloaded resources are stored to grails-app/assets where the Asset Pipeline plugin can easily find them.
  • The Asset Pipeline Plugin allows us to declare the dependencies inside the resources file such as this index file. The library resources are taken from the bower_components directory inside grails-app/assets folder. Don’t forget to run npm install && bower install first to fetch all the dependencies.
  • With all the dependencies in place we can just plug our JavaScript into the single page. If there are some Grails things to handle, such as the context root of the application than the index.gsp file is the place where put such customization.
  • Jasmine is one of the most popular JavaScript testing frameworks created by Pivotal Labs. It’s nearly as powerful as Spock as it does not only support the assertion but it also supports mocking and spying on object. It’s missing it’s syntactical sugar but with bit of creativity you can create data driven tests there as well.
  • The Jasmine tests are no rocket science. You have a suite starting with describe method call and feature methods starting with it. Module and inject methods comes from angular-mock.
  • The inject method comes from angular mock as well as the httpBackend service which helps testing the http request.
    We’re expecting that when we call method list on our service the list saved in the fixtures will be returned. These tests are very trivial but as the application would grow it will definitely become more complex.
  • We can also test some advanced interaction such as posting a data end returning the expected results. The assertion will fail if other than expected payload will be posted.
  • And here’s the minimal service which satisfies the test. As I’ve said, everything is about promises in AngularJS.
    It uses $http method to call our backend restful endpoint. If somethings goes wrong the promise is rejected otherwise each item is enhanced by useful functions such as cross or remove.
  • Well when we have a tests we need to run them somehow. So let’s take a look to the last tool in our front-end toolbox. Last tool inside is Karma. It’s a test runner like JUnit but as it’s dealing with JavaScript it needs to support running the tests in multiple browsers so you can assert that your application will run in for example Internet Explorer as well.
  • Configuration for Karma is bit more complex. This is the important part where’re using the library files downloaded using Bower, our javascript files which resides in assets/javascript, angular-mock library which we’re going to discuss in a moment, finally the food for our Karma - the javascript fixtures we’ve created in the backend tests and the javascript tests themselves.
  • The last missing bit are the templates. So let’s take a look at them.
  • This is a bit specific to AngularJS, other frameworks might do this in different way. The first you should do is minimize the content of index.gsp to minimum. For example by including template defined elsewhere in AngularJS.
  • Instead of keeping the template in the separate file you can inside javascript put it in the something called $templateCache, which a cache hit prior trying to load template as a file.
  • This is a simplified content of the template. There is a input field for a new item which is bound to model newItemText but the field is disabled while loading. You can see there are errors shown if something wrong happens in the backend. There is also a list of existing items. Each of them you can cross or trash.
  • The logic for the template is provided by earlslist.itemCtrl controller. This is piece of code of the controller just for illustration how the items service is used inside the controller.
  • We can easily test if controller works properly. You can see, we no longer to need to use http backend mock as with Jasmine you can actually do some Spock-like spying/mocking. For example return the items as we wish from the list method directly.
  • The logic for the template is provided by earlslist.itemCtrl controller. We can easily test if it works properly. You can see, we no longer to need to use http backend mock as with Jasmine you can actually do some Spock-like spying/mocking. For example return the fixtures list directly (not shown - inside the callFake function).
  • And we can test if the template renders as expected as well. For example given the same mocked controller it should show 13 items.
  • So with almost everything important set up we can test the function of the website.
  • We can actually use whatever test framework but Geb is great for functional testing so why not to use it.

    Here’s a minimal Geb configuration for running with FireFox browser which you place in test/functional folder.
  • We have only one page which will allow to enter new TODO items and show the existing ones. It will also show the errors if any happen.
  • So the last bit which needs to be tested is the function of the website itself.
    If you want to break long test scenario to smaller pieces you can do it with @Stepwise annotation. GebReportingSpec is useful one which will record the screenshot at the end of feature method execution and it also dumps the current dom to HTML file.
    And that’s it
  • http://4.bp.blogspot.com/_gAKC8zv9EIg/TMVw07EFGnI/AAAAAAAAAtU/UzszS2lUq5M/s1600/my-name-is-earl-my-name-is-earl-119293_1024_786.jpg
  • Well now answer for the major question. Will it play with CI?
  • It will. For example with Travis CI
  • as node is available for every language we can just declare the build as groovy build. before we run the tests we want to install all fronted NPM and Bower dependencies.
    For headless testing we need to create virtual display and then we can run all the Grails tests followed by Karma specification.

×