如何在 AngularJS 中对控制器进行单元测试(3)

那么要是你在调用$httpservice请求或是发送数据到服务端呢?还好,Angular提供了一种

$httpBackend的mock方法。这样的话,你就能自定义服务端的响应内容,又或是确保服务端的响应结果能和单元测试中的预期保持一致。

具体细节如下:

describe('MainCtrl', function() {
    var $scope, $rootScope, $httpBackend, $timeout, createController;
    beforeEach(inject(function($injector) {
        $timeout = $injector.get('$timeout');
        $httpBackend = $injector.get('$httpBackend');
        $rootScope = $injector.get('$rootScope');
        $scope = $rootScope.$new();


        var $controller = $injector.get('$controller');

createController = function() {
            return $controller('MainCtrl', {
                '$scope': $scope
            });
        };
    }));

afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
    });

it('should run the Test to get the link data from the go backend', function() {
        var controller = createController();
        $scope.urlToScrape = 'success.com';

$httpBackend.expect('GET', '/slurp?urlToScrape=http:%2F%2Fsuccess.com')
            .respond({
                "success": true,
                "links": ["http://www.google.com", "http://angularjs.org", "http://amazon.com"]
            });

// have to use $apply to trigger the $digest which will
        // take care of the HTTP request
        $scope.$apply(function() {
            $scope.runTest();
        });

expect($scope.parseOriginalUrlStatus).toEqual('calling');

$httpBackend.flush();

expect($scope.retrievedUrls).toEqual(["http://www.google.com", "http://angularjs.org", "http://amazon.com"]);
        expect($scope.parseOriginalUrlStatus).toEqual('waiting');
        expect($scope.doneScrapingOriginalUrl).toEqual(true);
    });
});

正如你所见,beforeEach call其实都很类似,唯一不同的是我们是从injector获取$httpBackend而并非直接获取。即使如此,创建不同的测试时还会有一些明显的不同之处。对初学者来说,会有一个afterEachcall 方法来确保$httpBackend在每次用例执行后不会有明显的异常请求。如果你观察一下测试场景的设置和$httpBackend方法的应用就会会发现有那么几点不是那么直观的。

实际上调用$httpBackend的方法也算是简单明了但还不够——我们还得在传值给$scope.$apply的方法中把调用封装到实际测试中的$scope.runTest方法上。这样在$digest被触发后才能处理HTTP请求。而如你所见直到我们调用$httpBackend.flush()方法后$httpBackend才会被解析,这也就保证了我们能在调用过程中去验证返回的结果是否正确(在上面的示例中,controller的$scope.parseOriginalUrlStatusproperty属性将被传递给调用者,我们也因此能实时监控)

接下来的几行代码都是在调用过程中检测$scopethat属性的断言。很酷吧?

提示:在某些单元测试中,用户习惯把没有$的范围标记为变量。这个在Angular文档中并没有强制要求或是过分强调,只是我在使用中为了提高可读性和一致性才使用$scopelike这种方式。

结论

也许这就是我做起来对其他人而言只是自然而然能做到的事情之一,但是学习使用Angular编写单元测试一开始对我而言确实是相当痛苦的。我发现自己对如何开始的理解大多来自互联网上各种博客文章和资源的拼拼凑凑,没有真正一致或明确的最佳实践,而是通过自然而然随意的选择。我想针对我最终得到的成果提供一些文档,以帮助那些也许还在坑里面挣扎的其他人,毕竟他们只是想要编写代码而已,而非不得不去了解Angular和Jasmine中所有的怪异特性和独特用法。因此我希望这篇文章能对你有些许帮助。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/94f45d3bbf16fd7085b9bb4a96483432.html