Tuesday, 18 March 2014

Testing Ext JS Controller using Jasmine Framework

The problem with Java Script development is debugging and testing. For debugging you have chrome debugger as a good tool. Once the application is debugged and it is working seamlessly, your work doesn't end there. There are certain tailoring and modifications which are always desired throughout the life cycle of an application. Making changes in one part of the Java Script code may introduce bugs and failures in other parts and can go undetected till someone encounters it. What is the way to avoid such unwanted and hidden failures.

The best way is to write certain test cases and re-run them after any change to the code to ensure that there is no break in the functioning of application. Jasmine framework is the one we are going to use. Google to know more, as we don't reinvent the wheel.
We have created a simple Stock Grid application (MVC) in the last post. You can go through it by clicking here. Now, I am going to write the a set of test cases for the controller.

Create folder app-test->lib->jasmine-1.3.1 and put the following three files into it.
  1. jasmine.css
  2. jasmine.js
  3. jasmine-html.js
Create folder app-test->specs and put the test file here which is as follows.

StockGridControllerSpec.js
describe("Stock Grid Controller", function() 
{
 var controller = null;   
 it('#1 -> Initialize controller : should be able to instantiate Stock Grid controller', function() 
 {
  controller = Application.getController('grid.StockGrid');
  // NB: We've created a controller outside of the lifecycle of the Application. We must manually initialise it.
  controller.init();
  // If we'd defined a launch method for controller, we'd need to call it here
  expect(controller).toBeTruthy();
 });
 
 it("#2 -> Testing refs : should have no valid references before making widgets", function() 
 { 
  expect(controller.getStockGrid()).toBeUndefined();
 });
 
 var store = null; 
 it("#3 -> Store Load : should be able to get no records loaded into store", function () 
 {
  store = controller.getStocksStore();
  expect(store.getCount()).toEqual(0);   
 });
 
 var widget = null
 it("#4 -> Creating Widget : should be able to create a widget, via the controller", function() 
 {
  widget = controller.getView('grid.StockGrid').create({ id: 'grid1' });    
  expect(widget).toBeTruthy();
 });
 
 it("#5 -> Firing event : should be able to fire events on the widget, and have them picked up by the controller using the xtype", function() 
 {
  spyOn(controller, 'loadStockStore');    
  widget.fireEvent('render');    
  expect(controller.loadStockStore).toHaveBeenCalled();  
 });
});

The code for StockGrid Controller is as follows.
Ext.define('Example.controller.grid.StockGrid',
{
 extend : 'Ext.app.Controller', 
 views : [ 'grid.StockGrid'], 
 models : ['Stock'], 
 stores : ['Stocks'], 
 refs :
 [
  { 
   ref: 'stockGrid', 
   selector: '#stock-grid' 
  },
 ],
 
 init : function()
 {
  var me = this;
  me.bindListeners();
  me.callParent(); 
 },
 
 bindListeners : function()
 {
  var me = this;
 
  this.control(
   {
    'stock-grid' :
    {     
     /* 
     render : me.loadStockStore
     WORKS, BUT IS NOT TESTABLE - 
     the spy will replace the implementation of loadStockStore, but won't change the listener     
     Therefore, 
     */ 
     render : Ext.Function.alias(me, 'loadStockStore')
    }
   }
  ); 
 },
  
 loadStockStore : function()
 {
  var me = this;
  me.getStocksStore().load();
 } 
});

Create app-test.js at same level as app.js
Ext.Loader.setConfig(
{
 enabled : true,  
 paths: 
 {
  /*
   In application we specify name as Example.store.Stock
   and as the name in Application in this file specifies Example,
   it searches Example folder.
   
   To replace Example with actual folder name app, following configuration 
   is added.
  */
  Example: 'app' 
 }
});

var Application = null;

Ext.onReady(function() 
{

 Application = Ext.create('Ext.app.Application', 
 {
  name: 'Example', //This name should be same as the application name

  controllers: ["grid.StockGrid"], //name of folder inside controllers and then controller file name

  launch: function() 
  {
   jasmine.getEnv().addReporter(new jasmine.HtmlReporter());
   jasmine.getEnv().execute();
  }
 });
});

Create tests.html at the same level as index.html
<html>
<head> 
 <title>Ex JS Grid Application (MVC) Test</title>
 <script src="ext-all.js"></script>
 <script src="app-test/lib/jasmine-1.3.1/jasmine.js" type="text/javascript"></script>
 <script src="app-test/lib/jasmine-1.3.1/jasmine-html.js" type="text/javascript"></script> 
 <link href="app-test/lib/jasmine-1.3.1/jasmine.css" rel="stylesheet" type="text/css"></link>
 <script src="app-test.js"></script>  
 <script src="app-test/specs/StockGridControllerSpec.js"></script>  
</head>
<body>
</body>
</html>

Finally just hit the test.html URL to verify that all the test results are positive.


When any changes are made anywhere in this application, you cans imply hit a URL and execute all the tests.Feel free to ask your doubts in comments to this post.

No comments:

Post a Comment

Your comments are very much valuable for us. Thanks for giving your precious time.