如何在Protractor测试中模拟对navigator.geolocation的调用

时间:2014-05-02 14:53:52

标签: angularjs unit-testing mocking protractor

假设你有一个Angular应用程序,它显示了一个地方列表。有一个按钮可以获取您当前的位置,点击该按钮可以根据您所在位置的距离对该列表进行排序,距离最近。

要在量角器中进行测试,您希望能够单击按钮并检查列表:

it('Should order items according to distance', function () {
    locButton.click();
    expect(...).toBe(...); // Check that the first item on the list
                           // the closest to the given lat/long

});

现在,假设按钮调用控制器中的方法,控制器调用服务中的方法,服务调用navigator.geolocation.getCurrentPosition()(并且,为了更好的衡量,该调用包含在一个promise中)。测试此方法的最佳方法是模拟对getCurrentPosition()的调用并返回特定的纬度和经度,以便具有所需的效果,一直支持链到页面输出。你如何设置那个模拟?

我尝试了this answer to a similar question about Jasmine中的方法,在navigator.geolocation上创建了一个间谍,结果如下:

ReferenceError: navigator is not defined

我还尝试使用类似于this answer的结果来模拟服务,结果为:

ReferenceError: angular is not defined

更新: 找到了解决方案所以我在下面回答了我自己的问题,但我真的,真的希望有更好的答案。

2 个答案:

答案 0 :(得分:3)

使用browser.executeScript()直接在浏览器中运行一些JavaScript,找到way to do it。例如:

describe('Testing geolocation', function () {
    beforeEach(function () {
        browser.executeScript('\
            window.navigator.geolocation.getCurrentPosition = \
                function(success){ \
                    var position = { \
                        "coords" : { \
                            "latitude": "37",
                            "longitude": "-115" \
                        } \
                    }; \
                    success(position); \
                }')
    });

    it('Should order items according to distance', function () {
        locButton.click();
        expect(...).toBe(...); // Check that the first item on the list
                               // the closest to the given lat/long
    });
});

这很有效,但很难看。我尽力使传递给browser.executeScript()的字符串尽可能可读。

修改

这是一个清理版本,有两个模拟成功和错误的功能:

describe('Geolocation', function () {
    function mockGeo(lat, lon) {
        return 'window.navigator.geolocation.getCurrentPosition = ' +
            '       function (success, error) {' +
            '           var position = {' +
            '               "coords" : {' +
            '                   "latitude": "' + lat + '",' +
            '                   "longitude": "' + lon + '"' +
            '               }' +
            '           };' +
            '           success(position);' +
            '       }';
    }

    function mockGeoError(code) {
        return 'window.navigator.geolocation.getCurrentPosition = ' +
            '       function (success, error) {' +
            '           var err = {' +
            '               code: ' + code + ',' +
            '               PERMISSION_DENIED: 1,' +
            '               POSITION_UNAVAILABLE: 2,' +
            '               TIMEOUT: 3' +
            '           };' +
            '           error(err);' +
            '       }';
    }


    it('should succeed', function () {
        browser.executeScript(mockGeo(36.149674, -86.813347));
        // rest of your test...
    });

    it('should fail', function () {
        browser.executeScript(mockGeoError(1));
        // rest of your test...
    });
});

答案 1 :(得分:1)

量角器测试是e2e,因此您实际上无法访问后端代码和结果。

我有一个类似的问题,当我点击表单中的提交时,我想看到我的“帖子”输出。

创建了这个,在dom中填充测试结果,所以你可以看到这样的后端内容。

不是最好的,但是没有其他方法可以做到这一点。

/////////////////////////////////////////////////////////////////
//markup added for testing
<div ng-controller="myTestDevCtrl"> 
    <button id="get-output" ng-click="getOutput()">get output</button> 
    <input ng-model="output" /> 
</div>
/////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////
//test controller to show ajax data coming out
myTestModule.controller('myTestDevCtrl', function($scope,dataProxy) {
    $scope.getOutput = function() {
        $scope.output = dataProxy.getData();
    }
})
//small service to capture ajax data
.service('dataProxy',function() {
    var data;
    return {
        setData : function(_data) {
            data = decodeURIComponent(_data);
        },
        getData : function() {
            return data;
        }
    }
})
.run(function($httpBackend,dataProxy) {
    //the office information post or 'save'
    $httpBackend.when('POST',/\/api\/offices/)
    .respond(function (requestMethod, requestUrl, data, headers) {
        //capture data being sent
        dataProxy.setData(data);
        //just return success code
        return [ 200, {}, {} ];
    });
});
//make myTestModule require ngMockE2E, as well as original modules
angular.module('myTestModule').requires = [
    'ngMockE2E'
];
/////////////////////////////////////////////////////////////////