0

I'm trying to test an Angular controller and mock a factory so that I can use it within this same test. I'm fairly new to Angular testing & have been having trouble figuring out how to this. My factory, doesn't use the $http rather the $q service, returns a promise. I'm also unsure of what to put inside my mock factory given that the factory's function calls return a promise.

My end goal is to call my mock factory from my controller and then check the two arrays in my controller for the data which is supposed to populate them. If you have any tips for restructuring my tests for testability, please do give feedback.

Angular Controller

    export class workListController {
      constructor(dataService, $q) {
        this.$q = $q;
        this.work = [];
        this.tasks = [];
        this.dataService = dataService;
        this.setup();
      }

      setup() {
        this.$q.all([this.dataService.getWorkItems(), this.dataService.getTasks()])
        .then(() => {
          this.work = this.dataService.getState().work;
          this.tasks = this.dataService.getState().tasks;
          this.dataService.addNumberOTasksToWork();
        });
      }

      tasksForWork(workId) {
        var workTasks = [];
        for (let task of this.tasks) {
          if (task.agf__Work__c === workId) {
            workTasks.push(task);
          }
        }
        return workTasks;
      };

    }

Angular Factory

  const dataService = ($q) => {
    let work = [];
    let tasks = [];
    let connection = new Connection{/**/};

    return { getWorkItems, getTasks, addNumberOTasksToWork, getState};
    function queryWrapper(query) {
      var deferred = $q.defer();
      connection.query(query)
        .then(function(result) {
          deferred.resolve(result);
        }, function(error) {
          deferred.reject(error);
        });

      return deferred.promise;
    }


    function getWorkItems() {
      return queryWrapper(`SELECT Id, ......`)
       .then((data) => {
         //data looks like this: {totalSize: 3, done: true, records: [......]}
         work = data.records;
       });
    }

    function getTasks() {
      return queryWrapper(`SELECT Id,...`)
       .then((data) => {
         //data looks like this: {totalSize: 3, done: true, records: [......]}
         tasks = data.records;
       });
    }

    function addNumberOTasksToWork() {
      work.forEach((workItem) => {
        workItem.numberOfTasks = 0;
      });
      work.forEach((workItem) => {
        tasks.forEach((taskItem) => {
          if (taskItem.agf__Work__c === workItem.Id) {
            workItem.numberOfTasks++;
          }
        });
      });
    }

    function getState(){
      return {work,tasks};
    }

  };

  export {dataService};

Test file

  import {workList} from './work-list.module.js';
  import {workListDirective} from './work-list.directive.js';
  import template from './work-list.html';
  import {workListController} from './work-list.controller.js';

  describe('AA_TaskBoard -  workList', function () {
    let $scope;
    let $controller;
    let $httpBackend;
    let mockDataService;

    beforeEach(angular.mock.module(workList.name));
    //trying to mock factory
    beforeEach(angular.mock.module(function($provide) {
      $provide.value('dataService', mockDataService);
      mockDataService = {
        getWorkItems: function(){
          //this should return a promise, but unsure of what to put here
          return {

          };
        },
        getTasks: function(){
          return {

          };
        }
      };

    }));


    beforeEach(inject(function(_$rootScope_, _$controller_, _$httpBackend_) {
      $rootScope = _$rootScope_;
      $controller = _$controller_;
      $httpBackend = _$httpBackend_;
    }));





  });
Clifford Fajardo
  • 1,357
  • 1
  • 17
  • 28
  • The code in controller `setup` operates on the service and calls its methods several times. This means that this logic belongs to the service, not controller. If controller has to do `$q.all` on a *single* service, this means that the service was designed improperly. All that controller should do is to call *one* service method and get the results in `then`. – Estus Flask Jun 10 '16 at 17:49
  • So do you suggest I do that $q.all in my factory and have all the data that I require in my controller be accessible via one call from my controller? For example have something like `getWorkandTasks.then( (..) =>{ }` in my controller? – Clifford Fajardo Jun 10 '16 at 18:07
  • That's correct. And if Works and Tasks were two different services, probably an aggregating service would be an answer. If the controller becomes difficult to test (and this one does), there's something wrong with it. – Estus Flask Jun 10 '16 at 18:19

0 Answers0