Tuesday 24 January 2017

Manual event triggering and event listening in angular


Manual event triggering and event listening in angular


We may come at situation where we need to manually trigger event  on some element or to listen existing events on DOM. This is very easy when we use JQuery . We all have been doing this since long.

Angular comes with Jquery light instead of JQuery .  We still can use JQuery but  not  advisable.  All we stick to JQuery light which comes as default.

In Angular things are simple as you can bind event listener in html template itself  for example ng-click. This  post is regarding situation where we need to manually trigger events or listen some existing events and how to cover that in test cases.

I will cover both manually triggering the event and normal event listener in controller.

Lets say you want to bind key down  to capture some events like CTRL + P or CTRL +V etc..

Use case 1 - Listening to some event listener in angular.

You can do in controller simply by below example. you can see in yellow line. So I covered one use
case that is listening to some event listener in angular.


class Controller {
  constructor( $document) {
    this.$document = $document;
  }

 $onInit() {
    this.$document.on('keydown', (e) => this.handleCtrlP(e));
  }

 handleCtrlP(event) {
    if (event.keyCode === 80 && (event.metaKey || event.ctrlKey)) {
      event.preventDefault();
      this.printPage();
    }
  }

Use case 2 - Manually triggering the event in angular

I will cover by writing the test case for above case. So i can show how to write the test case for the same and can cover second use case.

describe('handleCtrlP', () => {

    beforeEach(() => {
        sinon.spy(controller, 'handleCtrlPOfflinePrint');
        sinon.spy(controller, 'printPage');
        controller.$onInit();
      });

    it('should call printPage method when ctrl p triggered', () => {
        angular.element(controller.$document).triggerHandler({ type: 'keydown', keyCode: 80, ctrlKey:         true });
        expect(controller.handleCtrlP).to.have.been.called;
        expect(controller.printPage).to.have.been.called;
      });

     it('should not call printPage method when other than ctrl p kewdown gets trigeered', () => {
        angular.element(controller.$document).triggerHandler({ type: 'keydown', keyCode: 89,                       metaKey: true });
        expect(controller.handleCtrlP).to.have.been.called;
        expect(controller.printPage).not.to.have.been.called;
      });
});

You can notice how i am triggering the keyDown event with keyCode to achieve ctrl +P event. 
Same way you can trigger wherever you want either in your functional code or test case.

triggerHandler comes with JQuery light in angular.element.  You can trigger event on any html element. Just pass that element in argument. In our case its controller.$document  as we listened ctrl + p event on document element.


Hope this article is useful for Angular and specially for test cases.


Note :  I used ECMA 2015 and Angular 1.5 for coding in above example and Mocha + Sinon + Chai syntax for test cases:

Sunday 11 December 2016

Test case for angular $http Post and Get

                  AngularJs -  Test case for HTTP Post and Get


If you are developing code in Angular Js then it is most likely that you write method post and get for service calls.  How about writing test cases for this methods. 

Note : Below example uses ES6 JavaScript  and Mocha-Sinon-Chai Test suit.

HTTP Get:

lets understands with example:

      getCall() {
            return this.$http.get('../api/getcall').then(res => get(res, 'data.source'));
     }


Test case :  Its simple using whenGet. whenGet is provided by httpBackend mock api which  respond with given data once flush() is called.

 describe ('getCall' , => {
    it('should resolve  with correct data', () => {
           let res;
           const data = { source: 'testData' };
           $httpBackend.whenGET(u => u === '/api/getcall').respond(data);
           const promise = service.getCall().then((r) => res = r);
           $httpBackend.flush();
           expect(res).to.deep.equal('testData');
    });
});




HTTP Post:


lets understands with example:

      getPost() {
            return this.$http.post('/api/postcall', data, {
                  headers: {
                       'Content-Type': 'application/json',
                  },
            }).then((res) => get(res, 'data.data'));
          }


Test case :  This is the reason i am writing the blog.  You can notice the difference of test case of whenGet and below one. If you pass the function inside respond method then you have to return response with status code which 200 in below case.

 describe ('postcall' , => {   
   it('should resolve with correct data', () => {
          let res;
          $httpBackend.whenPOST(u => u === '/api/postcall')
                .respond((method, '/api/postcall', data) =>  [200, { data: { key: '11' } }]);           
           service.getPost({ data: 'submitionData' }).then((r) => res = r);
           $httpBackend.flush();
           expect(res).to.eql({ key: '11' });
    });
});



Above same can be written without respond code if do not pass the above method in respond function argument (blue color one).


 describe ('postcall' , => {   
    it('should resolve with correct data', () => {
          let res;
          $httpBackend.whenPOST(u => u === '/api/postcall')
                .respond({ data: { key: '11' } });
           service.getPost({ data: 'submitionData' }).then((r) => res = r);
           $httpBackend.flush();
           expect(res).to.eql({ key: '11' });
    });
});


Above same is applicable for whenGet with/without respond code.


when :

HTTP post and get can be written with $httpBackend.when   . Just we need to pass the method name along with URL.



 describe ('postcall' , => {    
   it('should resolve with correct data', () => {
          let res;
          $httpBackend.when( 'post', u => u === '/api/postcall')
                .respond({ data: { key: '11' } });
           service.getPost({ data: 'submitionData' }).then((r) => res = r);
           $httpBackend.flush();
           expect(res).to.eql({ key: '11' });
    });
});


You can resolve call with error status. Just  by doing respond(function () { return [500]; }) in above call.

This is all possible for other HTTP verbs like Delete, Put also.

Please refer the below JSfiddle demo for the same. I have not used ES2015. Easy to understand the test cases.

http://jsfiddle.net/vippatel90/8tcn38pr/

I hope this information will be useful  for all. I understand it worth to share :)



Friday 2 December 2016

Spying on Jasmine vs Mocha + Sinon + Chai


                           Spying on Jasmine vs Mocha + Sinon  + Chai


As a UI front end developer, many of us have come across the Jasmine framework for Js test cases and Mocha Sinon with Chai assertion Library.  We know that it almost impossible to write test case without SPY functionality of any Testing Framework. Let me tell you basic difference between above two framework when you make a SPY for test case.


1. Anonymous spy : 

creates an anonymous function that records arguments, this value, exceptions and return values for all calls. It does not call actual function.

Using Jasmine:


           object.method =  jasmine.createSpy(); OR

            spyOn( object, 'method' ); (from jasmine 2.0) 

*Important : SpyOn needs method to be present on object so not actually anonymous spy while jasmnie.createSpy does not require as it assigns empty anonymous function. 

In short if your try spyOn(object, 'method') without having method available on object then you will get error stating the method to be spied upon does not exist on object.

Using Mocha chai:  


     object.method =  sinon.spy();
            
             sinon.stub( object , 'method');

*Important :  sinon.stun needs  method to be present on object so not actually anonymous spy while sinon.spy() does not require as it assigns empty anonymous function.



2. Spy with Actual function call : 

calls actual implementation and allow to record arguments, this value, exceptions and return values for all calls. 

Using Jasmine:


              spyOn( object, 'method' ).add.callThrough()

Using Mocha chai:


             sinon.spy(object , 'method');   

             (Notice the difference between sinon.spy and sinon.stub)



3. Spy with stub function call : 

assign the stub function which can receive the arguments and can return the stub data. 


Using Jasmine:


      spyOn( object, 'method' ).add.callFake( function (args) { 
                  return  value;
             });            

             for just return the value :

              spyOn( object, 'method' ).add.returnValue(true);



Using Mocha chai: 


      sinon.stub( object, 'method', function (args) {
                   retrun value;
              }); 



conclusion:


I write the test cases in both framework and above noticed 
differences helped me a lot to quick writing of the test case. Hope you can get benefit of that. Enjoy :)