78

我正在寻找一种以编程方式即时更改 navigator.userAgent 的方法。在我尝试获得自动化 javascript 单元测试器的失败中,我放弃了并尝试开始使用 fireunit。立即,我撞上了使用实际浏览器进行 javascript 测试的一堵墙。

具体来说,我需要更改 navigator.userAgent 以模拟数百个 userAgent 字符串,以确保对给定函数的正确检测和覆盖。navigator.userAgent 是只读的,所以我似乎卡住了!如何模拟 navigator.userAgent?用户代理切换器(插件)可以切换FF的用户代理,但我可以在javascript中做到吗?

4

15 回答 15

120

尝试:

navigator.__defineGetter__('userAgent', function(){
    return 'foo' // customized user agent
});

navigator.userAgent; // 'foo'

在FF2和FF3中尝试过。

于 2009-08-20T15:46:04.407 回答
25

添加到Crescent Fresh 的解决方案中,重新定义navigator.userAgentgetter 似乎在 Safari 5.0.5 中不起作用(在 Windows 7 和 Mac OS X 10.6.7 上)。

需要创建一个继承自该navigator对象的新对象,并定义一个新的userAgentgetter 来隐藏原来的userAgentgetter navigator

var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });
于 2011-05-10T12:29:34.410 回答
24

以下解决方案适用于 Chrome、Firefox、Safari、IE9+ 以及 iframe:

function setUserAgent(window, userAgent) {
    if (window.navigator.userAgent != userAgent) {
        var userAgentProp = { get: function () { return userAgent; } };
        try {
            Object.defineProperty(window.navigator, 'userAgent', userAgentProp);
        } catch (e) {
            window.navigator = Object.create(navigator, {
                userAgent: userAgentProp
            });
        }
    }
}

例子:

setUserAgent(window, 'new user agent');
setUserAgent(document.querySelector('iframe').contentWindow, 'new user agent');
于 2014-11-12T13:36:51.127 回答
11

对于那些因为需要在单元测试中更改 userAgent 值的人,Tyler Long 的解决方案有效,但如果您想恢复初始 userAgent 或多次更改它,您可能需要将该属性设置为可配置:

function setUserAgent(userAgent) {
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return userAgent; // customized user agent
        },
        configurable: true
    });
}

// Now in your setup phase:
// Keep the initial value
var initialUserAgent = navigator.userAgent;
setUserAgent('foo');

// In your tearDown:
// Restore the initial value
setUserAgent(initialUserAgent);

否则你可能会遇到TypeError: Cannot redefine property错误。在 Chrome Headless 上为我工作。

于 2019-01-08T16:39:30.487 回答
9

使用 Object.defineProperty 应该会添加更多浏览器:

if (navigator.__defineGetter__) {
    navigator.__defineGetter__("userAgent", function () { 
        return "ua"; 
    });
} else if (Object.defineProperty) { 
    Object.defineProperty(navigator, "userAgent", { 
        get: function () { 
            return "ua";
        }
    });
}

此代码应该在Firefox 1.5+Chrome 6+Opera 10.5+IE9+中工作(并经过测试)。不幸的是,任何平台上的 Safari 都不允许更改 userAgent。

编辑:Safari 不允许更改 userAgent,但可以替换整个导航器对象,如上面另一个解决方案中所指出的那样。

于 2014-03-24T16:40:44.410 回答
4

Crescent Fresh 的回答是正确的。但是有一个问题:__defineGetter__已弃用:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineGetter _

已弃用 此功能已从 Web 标准中删除。尽管某些浏览器可能仍然支持它,但它正在被删除。不要在旧项目或新项目中使用它。使用它的页面或 Web 应用程序可能随时中断。

您应该defineProperty改用:

Object.defineProperty(navigator, "userAgent", { 
    get: function () { 
        return "foo"; // customized user agent
    }
});

navigator.userAgent; // 'foo'
于 2015-01-30T11:43:37.143 回答
4

为了更新这个线程,defineGetter 在 Jasmine 中不再工作,因为它已被弃用。但是我发现这允许我在 jasmine 中修改 navigator.userAgent 的 getter:

navigator = {
  get userAgent() {
    return 'agent';
  }
}

console.log(navigator.userAgent); // returns 'agent'

只要记住在茉莉花中完成测试后重置导航器对象

于 2015-06-23T15:43:41.647 回答
3

对于那些试图在 TypeScript 中做同样事情的人来说,这是解决方案:

(<any>navigator)['__defineGetter__']('userAgent', function(){
    return 'foo';
});

navigator.userAgent; // 'foo'

或同样的语言:

(<any>navigator)['__defineGetter__']('language', function(){
    return 'de-DE';
});
于 2016-10-20T11:32:26.510 回答
2

我想我会采取依赖注入的方法。代替:

function myFunction() {
    var userAgent = navigator.userAgent;
    // do stuff with userAgent
}

也许做类似的事情:

function myFunction(userAgent) {
    // do stuff with userAgent
}

function getUserAgent() {
    window.userAgentReal = +window.userAgentReal || 0;
    return [ navigator.userAgent ][window.userAgentReal++];
}

function getUserAgentMock() {
    window.nextUserAgentMock = +window.nextUserAgentMock || 0;
    return [
        'test user agent1',
        'test user agent2',
        'test user agent3'
    ][window.nextUserAgentMock++];
}

var userAgent;
while (userAgent = getUserAgent()) {
    myFunction(userAgent);
}

然后,您可以通过以下方式“模拟” getUserAgent()

function getUserAgentReal() { // formerly not 'Real'
    // ...
}

function getUserAgent() { // formerly 'Mock'
    // ...
}

这个设计仍然不是完全自动化的(你必须手动重命名 getter 来执行你的测试),它给像操作这样简单的东西增加了一堆复杂性navigator.userAgent,我不确定你如何真正识别任何中的错误myFunction,但我只是想我会把它扔在那里给你一些想法如何处理这个问题。

也许这里提出的“依赖注入”的想法可以以某种方式与 FireUnit 集成。

于 2009-08-20T16:43:43.590 回答
1

以上答案不适用于 PhantomJS + TypeScript。下面的代码对我有用:

var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });
于 2017-08-22T13:29:49.147 回答
1

这个话题晚了,但是对于 Karma + Jasmin 和 Typescript 并且想要设置 userAgent 属性,这将做到:

describe('should validate YYYY-MM-dd format only on IE browser', () => {
    // this validator has a specific condition to work only in IE11 and down
    (window as any).navigator.__defineGetter__('userAgent', function () {
      return 'MSIE';
    });

...
// rest of the test

});

这篇文章有帮助:https ://www.codeproject.com/Tips/1036762/Mocking-userAgent-with-JavaScript

于 2018-08-06T14:15:49.967 回答
1

试试这个它对我有用,没有皮棉问题

Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });
于 2020-01-23T10:13:57.507 回答
0

navigator.userAgent是一个只读字符串属性,因此无法对其进行编辑

于 2009-08-20T15:39:23.343 回答
-1

通过 defineGetter 在 Firefox 和 Opera 上更改 navigator.userAgent

navigator.__defineGetter__('userAgent', function(){
    return( "iPhone 5" );
});

alert( navigator.userAgent ); //iPhone 5

通过对象实例更改 IE 和 Opera 上的 navigator.userAgent

var navigator = new Object; 
navigator.userAgent = 'iPhone 5';

alert( navigator.userAgent ); //iPhone5

好消息是,如果您使用 IE 浏览器控件,您可以通过 execScript 双重欺骗 HTTP 请求和 JavaScript navigator.userAgent

WebBrowser1.Navigate "http://example.com", , , , "User-Agent: iPhone 5" & vbCrLf

WebBrowser1.Document.parentWindow.execScript ("var navigator=new Object;navigator.userAgent='iPhone 5';")
WebBrowser1.Document.parentWindow.execScript ("alert(navigator.userAgent);") 'iPhone 5
于 2014-05-06T22:45:28.060 回答
-3

不,我怀疑你可以在 javascript 中做到这一点。但是使用 Firefox 的用户代理切换器,您可以测试任何您想要的用户代理,那么为什么不直接使用呢?

于 2009-08-20T15:38:09.447 回答