Showing posts with label unit-test. Show all posts
Showing posts with label unit-test. Show all posts

1/18/17

Unit Tests In Angular2 - part 3 (Component That Uses Service)

In previous post we've learned how to test the angular2 component.
That was very nice and we have a lot of fun, but everybody knows that in real life many components may use services to bring data from server.
Lets demonstrate it on our user component:
Lets make it to display friends list of this user:



@Component({
  selector: 'my-app',
  template: `<span>name: <strong>Victor Savkin</strong></span>
   <hr/>
   <strong>Friends:</strong>
   <ul>
     <li *ngFor="let friend of friends">
       {{friend}}
     </li>
   </ul>`
})
export class App {
   
    public friends:string[]=[]
    constructor(private usersService:UsersService) {
    }
    ngOnInit(): void {
       this.usersService.getUsers().subscribe(
         users => {
           this.friends=users;
         },
         error=>console.log(error) 
       );
    }
}


And the code of UsersSerivice:

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class UsersService {
  constructor(private http:Http) { }
  
  getUsers(){
    return this.http.get('api/users.json')
      .map(response => response.json());
  }
}


So How To Test This Thing?

Lets say we want to test that request populates the "friends" section with friends names.
Since it is bad practice to make real http request for testing purposes - we need to create a mock of UsersService:


import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
  
  class MockUsersService {
    public get() {return  Observable.of(['John', 'Steve']);}
  }

Once we have the mock of UsersService -lets inject it into the module instead of usersService:

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [UserComponent], 
      providers:[// <--HERE IS THE MAGIC!!!
       { provide: UsersService, useClass: MockUsersService}
      ],
      imports: [ HttpModule,BrowserModule ]
    })
    .compileComponents();
  }));

Great, inst it?
Now we can test the component and see if friends are displayed as planned:

  beforeEach(() => {
    fixture = TestBed.createComponent(UserComponent);
    comp = fixture.componentInstance;
    comp.user = new UserModel('Vasia','Kozlov')
    de = fixture.debugElement.query(By.css('span'));
    element = fixture.nativeElement;// to access DOM element
  });

  it('should have list of friends displayed inside UL',  async(() => {
    fixture.detectChanges();
    fixture.whenStable().then(() => { 
      expect(element.querySelector('li:first-child').innerText).toBe('John');
      expect(element.querySelector('li:last-child').innerText).toBe('Steve');
    })
  }));

you can see the full code on this plunker

1/5/17

Unit Tests In Angular2 - part 1 (Get Started)

Get Started

Here is link to amazing tutorial for someone who wanted to learn from real angular developers...
Angular 2 has it's way for writing unit tests, it is pretty resembles the angular1. The same jasmine, only in typescript

Example of Testing Person Model

Lets start with models since they are usual javascript objects:
(person-model.ts):


export class PersonModel {

    constructor(
        public firstname: string = '',
        public lastname: string = ''
    ) {};

}

So here how the tests should look:
(person-model.spec.ts):

import {PersonModel} from './person-model';

describe('PersonModel', () => {

  it('should return the correct properties', () => {

      var person = new PersonModel();
      person.firstname = 'Shlomo';
      person.lastname = 'The King';

      expect(person.firstname).toBe('Shlomo');
      expect(person.lastname).toBe('The King');

  });

});

Nothing complicate here...

Testing Angular2 Service

Next step I want to show how to test some service.
Services are usually built for send http requests and bring data:


import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class UsersService {
  constructor(private http:Http) { }
  
  get(){
    return this.http.get('api/users.json')
      .map(response => response.json());
  }
}

The Users service has only one method - get
So, this is how the tests should look:
users.service.spec.ts

import {inject, TestBed, async} from '@angular/core/testing';
import {HttpModule} from '@angular/http';

import {UsersService} from './users.service'
describe('Service: UsersService', () => {
  let service;
  
  //setup
  beforeEach(() => TestBed.configureTestingModule({
    imports: [ HttpModule ],
    providers: [ UsersService ]
  }));
  
  beforeEach(inject([UsersService], s => {
    service = s;
  }));
  
  //specs
  it('should return available users (async)', async(() => {
    service.get().subscribe(x => { 
      expect(x).toContain('sasha');
      expect(x).toContain('vasia');
      expect(x).toContain('pasha');
      expect(x.length).toEqual(3);
    });
  }));
  

}) 

Note that I'm using TestBed utility to initiate ngModel and the service with injected Http.
here is working plunker

6/8/15

Building & testing user-menu directive part 2

testing

first test

after we have build user menu directive in previous post we may want to test it...
It is a good practice to test things you did. (Actually better practice is to test things before you did them, but lets leave TDD and focus on only T...) So lets start doing some testing:
1. lets test firstly that our directive loading a template correctly:


describe('Testing user menu directive', function() {
  var $scope = null;
  var $compile;

  //you need to indicate your module in a test
  beforeEach(module('plunker'));
  beforeEach(inject(function($templateCache, _$compile_) {
    $compile = _$compile_;
    var directiveTemplate = null;
    var req = new XMLHttpRequest();
    req.onload = function() {
        directiveTemplate = this.responseText;
    };
 
    // Using `false` as the third parameter to open() makes the operation synchronous.
    req.open("get", "userMenuTpl.html", false);
    req.send();
    $templateCache.put("userMenuTpl.html", directiveTemplate);
  }));
  
  beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new();

  }));

  it('should display login menu', function() {
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest()              
    expect($(element).find('a:eq(0)').text()).toContain('Sign in');
  });  
});

So far so good...

Slight Modifications

Now to more complicate things - lets add "signout" functionality which will cause our directive to change its view dynamically:


var app = angular.module('plunker', [])
.directive('userMenu', function ($templateCache) {
    return {
        restrict: 'E', 
        templateUrl:'userMenuTpl.html',
        controller: function ($scope, userSvc) {
            if (userSvc.isAuthenticated()) {
                $scope.profile = userSvc.getProfile(); 
            }
            $scope.getprofile = function(){
              return  userSvc.getProfile()
            }
            $scope.signout = function(){
              userSvc.logout();
            }
        }
    }
})
.factory('userSvc', function () {
      
    var _isAuthenticated = false, _profile= null;
    return {
      isAuthenticated: function() {
        return _isAuthenticated;
      },
      getProfile: function() {
        return _profile
      },
      login: function(){
        _isAuthenticated = true;
        _profile = {username:'Shlomo'}
      },
      logout:function(){
        _profile = _isAuthenticated = false;
      }
    }
})

lets modify the template also


<ul class="nav navbar-nav navbar-right" ng-if="getprofile()">
  <li>
    <a href="/profile"><i class="glyphicon glyphicon-user">
      </i> {{profile.username}}
    </a>
  </li>  
  <li style="border-left:1px solid #fff">
     <a href ng-click="signout()">Sign out</a>  
  </li>
</ul>

<ul class="nav navbar-nav navbar-right" ng-if="!getprofile()">
  <li>
    <a href  ng-click="open()">Sign in</a>  
  </li>  
  <li>
    <a style="border-left:1px solid #fff" href="/link-to-login">Sign Up</a>  
  </li>
</ul>

more tests

So now our directive able to change it view after user clicking on some link. lets test those behaviors:


  //...
  it('when logged in should show "sign out" link and user name', function() {
    userSvc.login();
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest()              
    expect($(element).find('a:eq(1)').text()).toContain('Sign out');
    expect($(element).find('a:eq(0)').text()).toContain('Shlomo');
  });
  
  it('after loggin out- should show "Sign in" link again', function() {
    userSvc.login();
    element = $compile('<user-menu></user-menu>')($scope);
    $scope.$digest();              
    expect($(element).find('a:eq(0)').text()).toContain('Shlomo');
    expect($(element).find('a:eq(1)').text()).toBe('Sign out');
    element.find('a').triggerHandler('click');
    expect($(element).find('a:eq(0)').text()).toBe('Sign in');
  }); 
 //...

View it on plunker

Hope you have good time reading

5/13/15

Building & testing user-menu directive part 1

Part1


scenario

Consider the following scenario:
At the right of top navbar of the application screen, user should see his username   and "sign out" link while he is loggedin:

loggedin view


and when he is  not logged in - "signin" and "singnup" links should be displayed.

not-loggedin view



directive

Lets build simple directive user-menu  which will be placed into top navbar of our page:


<ul>
    <user-menu></user-menu>
</ul>


usermenu.js

angular.module("myApp")
.directive('userMenu', function () {
    return {
        restrict: 'E', 
        templateUrl: 'usermenuTpl.html',
        controller: function ($scope, userSvc) {
            if (userSvc.isAuthenticated()) {
                $scope.profile = userSvc.getProfile(); 

           }
        }
    }
})



userMenuTpl.html

<ul class="nav navbar-nav navbar-right" ng-if="profile">
  <li>
    <a href="/profile"><i class="glyphicon glyphicon-user">
      </i> {{profile.username}}
    </a>
  </li>  
  <li style="border-left:1px solid #fff">

     <a href="/link-to-logout">Sign out</a>  
  </li>
</ul>

<ul class="nav navbar-nav navbar-right" ng-if="!profile">
  <li>
    <a href  ng-click="open()">Sign in</a>  
  </li>  
  <li>
    <a style="border-left:1px solid #fff" href="/link-to-login">Sign Up</a>  
  </li>
</ul>



All things are simple - directive consists from 2 templates, and display each wether "profile" property is set or not.
"Profile" property will came from some User service (which usually should access to DB, but for being simple lets fake it)


userSvc.js

angular.module("myApp")
        .factory('userSvc', function () {
            return {
             isAuthenticated: function () {
                return true;                
             },
             getProfile: function () {
                return {username: 'Shlomo'};
             }
           }
        })



for testing manually lets see what happens when we changing code of isAuthenticated method so it returns false. Yes - we see signin & signup links.
So far so good
Hope you have fun reading

Part 2

4/30/15

Cool Trick For Unit Testing The Angular Controller

Almost always when writing unit tests for controller we need somehow to handle the fact that controller calls to some service/factory.

Lets look at following controller:



angular.module('siteApp')
        .controller('RegisterCtrl', function (
        $scope,
         auth,
         $location,
         $interval) {

            $scope.user = {};
            $scope.messages = [];


            var success = function(){
                $scope.messages.push({
                    type:'success',
                    text:'Congratulations,You are registered user!'
                });             
                $interval(function(){$location.path('/login')},6000,1)
            }, error =function(){
                $scope.messages.push({
                   type:'danger',
                   text:'error occurred during registration'
                });                
            };

            $scope.save = function(){
                auth.register($scope.user).then(success, error);         
            }

  });



The controller uses "auth" service and its "register" method, which sending a request.
We can mock the request with $httpBackend service which test if request is sended.


it('after clicking "save" some request should be sent', function () {
  initCtrl()
  $httpBackend.expectPOST('/api/register').respond(200, {});
  $scope.save();
  $httpBackend.flush();
});


Since we trying now to test the logic of the controller (not the service)- I think better practice is to mock the  service method and only test if it been called with proper parameters:



it('after clicking "save" auth.register should call', function () {

    initCtrl()
    spyOn(auth, 'register').andReturn($q.when()); 
    $scope.save();
    expect(auth.register).toHaveBeenCalledWith($scope.user);

    $scope.$digest();   //important!!!
    expect($scope.messages.length).toBe(1)


});


Notice that for testing the messages array you need 

  $scope.$digest(); 

statement for update the scope.

Hope you have fun reading...


Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...