Search code examples
javascriptangularjsunit-testingkarma-mochagulp-karma

TypeError: Cannot read property 'firstName' of undefined Unit Test AngularJS


I am stuck to do the Unit Test with Karma, I don't have idea how to the unit test because it's my first time. I am using AngularJS and the unit test is Karma.

The thing is this: I'm using a service to obtain the firstName, lastName and PhoneNumber of the customer to show in my form, and it works without any problem BUT, when I'm trying to do the unit test the error is always this:

directionFormulation component should load customer profile FAILED
        TypeError: Cannot read property 'firstName' of undefined

directionFormulation.js

  function directionFormulationController(event, customer, resolveLocation, order) {
    this.$onInit = onInit;
    this.input = this.input || {};

    function onInit() {
      loadCustomerData();
    }

    function loadCustomerData() {
      this.input.firstName = order.customer.firstName;
      this.input.lastName = order.customer.lastName;
      this.input.phoneNumber = order.customer.phoneNumber;

    }
  }
})();

Unit test: directionFormulation.spec.js:

  it('should load customer data', function () {
    var emptyFirstName = { firstName: 'something'};

    component.$onInit();
    order.customer.firstName = { firstName: 'Something'};
    order.customer.lastName = { lastName: 'Something' };
    order.customer.phoneNumber = { phoneNumber: 55555555};
    // component.input = {
    //   firstName: 'something',
    //   lastName: 'something',
    //   phoneNumber: 55555555
    // };

    component.loadCustomerData();
    $rootScope.$apply();
    component.input.firstName = newFirstName;

    expect(component.input.firstName).to.be.equal({firstName: 'something'});
    expect(component.input.lastName).to.be.not.empty;
    expect(component.input.phoneNumber).to.be.null;

  });
});

Solution

  • You are injecting order into your controller, so you will need to "mock" out order for your unit test:

    describe('addressForm component', function () {
      var component;
      var scope;
      var order;
    
      beforeEach(function () {
        bard.appModule('shopping.address');
        bard.inject('$rootScope', '$componentController', '$q', 'resolveLocation', 'customer', 'event','order');
        order = {
          customer: {
            firstName: 'Joe',
            lastName: 'Smith',
            phoneNumber: '416-555-1234'
          }
        };
        scope = $rootScope.$new();
        component = $componentController('addressForm', { 
          $scope: scope,
          order: order
        });
      });
    
      it('should be attached to the scope', function () {
        expect(scope.addressForm).to.be.equal(component);
      });
    
      it('should load customer profile', function () {
        component.$onInit();
        component.loadCustomerProfile();
    
        expect(component.input.firstName).to.be.equal(order.customer.firstName);
        expect(component.input.lastName).to.be.equal(order.customer.lastName);
        expect(component.input.phoneNumber).to.be.equal(order.customer.phoneNumber);
      });
    });
    

    I'd like to highlight a few other issues:

    1. Your first test asserting expect(scope.addressForm).to.be.equal(component); is not going to pass. AddressFormController is the name of your controller, and a controller is a property on a component.

    2. I'm not sure what bard refers to in your test and not sure whether appModule is a property on your bard instance. Here is a sample of a component test set up of mine: https://gist.github.com/mcranston18/0ded29eca9a53efeb945736b0a053061

    3. I would recommend this resource to learn a bit more about testing component controllers: http://www.codelord.net/2017/01/09/unit-testing-angular-components-with-%24componentcontroller/