zoukankan      html  css  js  c++  java
  • AngularJS测试二 jasmine测试路由 控制器 过滤器 事件 服务

    测试应用

    1.测试路由
    我们需要检测路由是否在运作,是否找到了,或者是404了。我们要确认路由事件触发了,预期的模板是否真的加载了。既然路由会改变页面的地址(URL)和页面内容,我们需要检测路由是否被加载了,页面是否找到了,在这中间发生了什么。
    一段简单的路由代码:
    angular.module('myApp', ['ngRoute'])
    .config(function($routeProvider) {
    $routeProvider
    .when('/', {
    templateUrl: 'views/main.html',
    controller: 'HomeController'})
    .when('/login', {
    templateUrl: 'views/login.html',
    controller: 'LoginController'})
    .otherwise({redirectTo '/'});
    })
    测试路由我们需要做以下几件事:
    (1)注入$route、$location和$rootScope服务。
    (2)建立一个模拟的后端来处理XHR,获取模板代码。
    (3)设置一个地址,运行一个$digest生命周期
    Step1:在测试中引用服务,并设置一个模拟后端,从templateUrl获取模板。可以用$httpBackend来创建断言来判断指定模板被加载了.
    describe('Routes test', function() {
    // 在测试中模拟模块
    beforeEach(module('myApp'));
    var location, route, rootScope;
    beforeEach(inject(
    function(_$location_, _$route_, _$rootScope_) {
    location = _$location_;
    route = _$route_;
    rootScope = _$rootScope_;
    }));
    describe('index route', function() {
    beforeEach(inject(
    function($httpBackend) {
    $httpBackend.expectGET('views/home.html')
    .respond(200, 'main HTML');
    }));
    // 我们的测试代码放在这里
    });
    });
    Step2: 为了用单元测试来测试路由,我们需要模拟路由在生产中的运作。路由借助于digest生命周期来运行,当location被设置以后,它使用一个 digest循环周期来处理路由,改变页面内容,然后结束本次路由。了解了这些之后,我们就需要解释测试中路径的变更。
    在测试中,我们打算在应用的index路由上测试两个状态:当用户导航到首页时,指定的控制器会给他们显示首页;当用户导航到一个未知路由时,他们会按照在otherwise函数中定义的那样被带到首页。
    我们可以通过建立$location服务来传递路径的方式测试这些条件。为了触发location请求,我们要运行一个digest周期(在$rootScope上),然后检测控制器是符合预期的(在本例中,是“HomeController”)。
    it('should load the index page on successful load of /',
    function() {
    location.path('/');
    rootScope.$digest(); // 调用digest循环
    expect(route.current.controller)
    .toBe('HomeController')
    });
    it('should redirect to the index path on non-existent
    route', function() {
    location.path('/definitely/not/a/_route');
    rootScope.$digest();
    expect(route.current.controller)
    .toBe('HomeController')
    });
    2.测试控制器
    在建立单元测试的过程中,需要确保:
    建立了测试来模拟模块;
    用一个已知的作用域实例来存储控制器的一个实例;
    基于作用域来测试我们的预期。
    要初始化一个控制器实例,需要使用$new()方法从$rootScope创建某作用域的一个新实例。这个新实例会建立Angular在运行时使用的作用域继承。有了这个作用域,就可以初始化一个新的控制器,把这个作用域作为控制器的$scope传递过去。
    describe('Unit controllers: ', function(){
    // 模拟myApp模块
    beforeEach(module('myApp'));
    describe('FrameController', function() {
    // 局部变量
    var FrameController, scope;
    beforeEach(inject(
    function($controller, $rootScope) {
    // 创建子作用域
    scope = $rootScope.$new();
    // 创建FrameController的新实例
    FrameController = $controller('FrameController',
    { $scope: scope });
    }));
    // 我们的测试代码放在这里
    });
    });
    在FrameController里,我们有一个时钟在应用顶部显示当前时间。我们也可以访问一个用户和他的时区。具体的控制器的代码如下
    angular.module('myApp.controllers', [])
    .controller('FrameController',
    function($scope, $timeout) {
    $scope.time = {
    today: new Date()
    };
    $scope.user = {
    timezone: 'US/Pacific'
    }
    var updateClock = function() {
    $scope.time.today = new Date();
    };
    var tick = function() {
    $timeout(function() {
    $scope.$apply(updateClock);
    tick();
    }, 1000);
    }
    tick();
    });
    我们要测试控制器的两个功能:时间已被定义;用户已被定义,并且有时区。
    it('should have today set', function() {
    expect(scope.time.today).toBeDefined();
    });
    it('should have a user set', function() {
    expect(scope.user).toBeDefined();
    });
    再举个简单易懂的例子:
    //创建一个scope对象,true表示它不会继承父级的变量,以免带来困扰
    var scope=$rootScope.$new(true);
    //找到SomeController,并对scope进行初始化
    $controller('$SomeController',{$scope:scope});  //在这里也可以注入其他被mock的service或resolve的对象
    //期待scope上应该出现一个name属性,其值为some one
    $expect(scope.name).toEqual('some one');
    //期待scope上应该出现一个greeting函数,调用它后的值为hello,some one
    $expect(scope.greeting()).toEqual('hello,some one');
    3.测试工厂和服务
    Service可以是函数,可以是类,也可以是变量,所以测试起来没什么固定的形式,但是步骤和原理是相似的。
    注入这个Service,把它保存为一个变量;
    如果是函数,就调用它;如果是类,就new它,然后检查成员;如果是变量,就对比它的值。
    var myClass;
    //注入
    beforeEach(inject(function(_MyClass_){
         MyClass=_MyClass_;   //赋给外部变量,以便后面的测试中使用
    }));
    it('someMethod should be 2',function(){
         var obj=new MyClass();
         expect(obj.someMethod()).toEqual(2);
    });
    4.测试过滤器
    首先,要访问过滤器,只需简单地把$filter服务注入到我们的测试中。这样我们就得到了一个在此过程中查找过滤器的途径:
    describe('Unit: Filter tests', function() {
    var filter;
    // 在测试中模拟我们的引用
    beforeEach(module('myApp'));
    beforeEach(inject(function($filter) {
    filter = $filter;
    }));
    });
    有了对控制器的访问,在过滤器的输出上设置预期就是很容易的事了。
    it('should give us two decimal points',
    function() {
    expect(filter('number')(123, 2)).toEqual('123.00');
    });
    5.测试模板
    测试模板时,我们着重于确保:特定的内容模板被加载了,模板中特定的数据也在视图中显示了。
    可以建立一个断言:模板正常加载了。要做到这个,需要建立测试来期望对主页模板的一个请求,并且执行一个视图的变更来验证它是不是真的加载了。
    describe('Unit: Templates', function() {
    var $httpBackend,
    location,
    route,
    rootScope;
    beforeEach(module('myApp'));
    beforeEach(inject(
    function(_$rootScope_, _$route_, _$httpBackend_, _$location_){
    location = _$location_;
    rootScope = _$rootScope_;
    route = _$route_;
    $httpBackend = _$httpBackend_;
    }));
    afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
    });
    // 我们的测试代码放在这里
    });
    现在,可以建立测试来反映导航到应用不同部分时的预期。
    it('loads the home template at /', function() {
    $httpBackend.expectGET('templates/home.html')
    .respond(200);
    location.path('/');
    rootScope.$digest(); // 调用digest循环
    $httpBackend.flush();
    });
    it('loads the dashboard template at /dashboard', function() {
    $httpBackend.expectGET('templates/dashboard.html')
    .respond(200);
    location.path('/dashboard');
    rootScope.$digest(); // 调用digest循环
    $httpBackend.flush();
    });
    注意,我们并未在测试中返回一个模板(是.respond(200),而不是.respond(200, "<div></div>"))。鉴于我们只是在验证模板是否在请求中加载了,没有必要担心到底显示了什么。
    6.测试事件
    当对事件的触发进行单元测试时,我们感兴趣的是它们究竟调用了什么,还有是否真的调用了正确的事件。其次,我们主要关心处理程序有它们需要的数据。
    使用Jasmine的辅助方法spnOn()可以非常容易地建立事件测试。设想我们在测试一个控制器,它触发了一个$emit函数。基于这个函数,我们可以建立一个预期:事件被触发了,并且用我们所感兴趣的任意参数调用了。
    describe('myApp', function() {
    var scope;
    beforeEach(angular.mock.module('myApp'));
    beforeEach(angular.mock.inject(function($rootScope) {
    scope = $rootScope.$new();
    });
    });
    测试建好之后,就可以简单地在作用域上为$emit或者$broadcase事件设置一个spyOn()调用了。

    // ...
    });
    it('should have emit called', function() {
    spyOn(scope, "$emit");
    scope.closePanel(); // 示例
    // 或者任意可能导致emit被调用的事件
    expect(scope.$emit)
    .toHaveBeenCalledWith("panel:closed",
    panel.id);
    });
    我们也可以测试事件:设置一个事件触发后调用的$on()监听器。要执行$broadcast方法,
    可以简单地在作用域上调用$broadcast,并且对事件将会导致的作用域变化建立一个预期。
    // ...
    it('should set the panel to closed state',
    function() {
    scope.$broadcast("panel:closed", 1);
    expect(scope.panel.state).toEqual("closed");
    });

  • 相关阅读:
    网页表格或div层在网页中被撑开解决之道
    jquery把给定的json自动生成多级下拉框
    jquery理想菜单实现(显示全国省市区分级效果)
    正则表达式记录
    jQuery自定义插件
    js数组及其常用方法
    vue自定义组件
    GET和POST
    可变对象和不可变对象
    js 不同元素的同一属性运动
  • 原文地址:https://www.cnblogs.com/lyy-2016/p/5714399.html
Copyright © 2011-2022 走看看