Tuesday, April 21, 2015

How to test private function in Angularjs

These days, I am working on unit tests of an AngularJS application. But it drives me crazy when I encountered private functions.
I  read the two articles by Philip Walton: 
My idea was inspired from these two articles.
It seems like whether and how to test the private function in Javascript is still a controversial topic.
For me, I choose to do it. There are two reasons:
First of all, some of the code are not written by me. I'd like to play around with my test code rather than changing others' work. And we haven't decided whether it's OK to make those code public.
Second of all, there are many private functions, I don't think it's good to skip all of them.

A brief instruction of the app I am working on

The app is built with gulp. We set the task

gulp test

for unit test.

In the app code, we have the following structure :
angular.module('MainModule').controller('MainCtrl',MainController);

mainController.$injct = ['dep1','dep2']

function MainController(dep1,dep2){
  var vm = this;
  var name = '';
  var isMember = false;

  vm.somePubFuncs = function(){
    // call those private functions from public function
  }

  function updateMemberBenefitTable(){
     // dosomething here
  }

  function loadMemberBenefitTable(){
    // dosomething here
  }
}

I will test the updateMemberBenefitTable() and loadMemberBenefitTalbe().

My basic idea of testing the private functions

Adding some public interfaces for the private function at the beginning of test, and strip them at the end of the test.
I use gulp-insert-lines for adding and gulp-strip-code for stripping.

I added the comments pair
/*test-code-insert-here*/  /*end-test-code-insert-here*/
i.e.

angular.module('MainModule').controller('MainCtrl',MainController);

mainController.$injct = ['dep1','dep2']

function MainController(dep1,dep2){
  var vm = this;
  var name = '';
  var isMember = false;

  vm.somePubFuncs = function(){
    // call those private functions from public function
  }

  /*test-code-insert-here*/
  /*end-test-code-insert-here*/

  function updateMember--BenefitTable(){
     // dosomething here
  }

  function loadMemberBenefitTable(){
    // dosomething here
  }
}

The interfaces will be inserted into the pair of comments.

The gulp task for unit test is:
var insertLines = require('gulp-insert-lines');
var stripCode = require('gulp-strip-code');

gulp.task('test',function(){

  // This section is to add the interfaces of the private function into the source file
  var str = '/*--test-only*/'+'\n'+
            'vm._updateMemberBenefitTable = updateMemberBenefitTable;'+'\n'+
            'vm._loadMemberBenefitTable= loadMemberBenefitTable;'+'\n'+
            '/*--end-test-only*/';
  gulp.src('path/of/MainCtrl')
    .pipe(insertLines({
      'after':/\/\*test-code-insert-here\*\/$/,
      'lineAfter':str
    }))
    .pipe(gulp.dest('dest'));
  //END This section is to add the interfaces of the private function into the source file
  
  return gulp.src('testfile.js')
    .pipe($.karma({
      configFile: 'karma.conf.js',
      action: 'run'  
    }))
    .on('end',function(){
      // This section is to strip off the interfaces of the private function 
      //at the end of the unit test
      gulp.src('path/of.MainCtrl')
        .pipe(stripCode({
          start_comment:'--test-only',
          end_comment:'--end-test-only'
        }))
        .pipe(gulp.dest('dest')); 
      //END This section is to strip off the interfaces of the private function 
      //at the end of the unit test
}) .on('error',function(err){ throw err; })


Please keep in mind that the /*--test-only*/  /*--end-test-only*/ comments pair is very important. Because later on, when gulp-strip-code is stripping the interfaces, the comments pair will be stripped as well. If you use /*test-code-insert-here*/ /*end-test-code-insert-here*/ pair for stripping, you will end up with being not able to find them at the next run of gulp test.