我正在寻找一种以编程方式动态更改navigator.userAgent的方法。在我尝试获得自动javascript单元测试器失败的尝试中,我放弃并试图开始使用fireunit。我立即猛烈抨击了使用实际浏览器进行javascript测试的一面墙。
具体来说,我需要更改navigator.userAgent来模拟几百个userAgent字符串,以确保对给定函数的正确检测和覆盖。 navigator.userAgent只读,所以我好像卡住了!我怎么能模拟navigator.userAgent?用户代理切换器(插件)可以切换FF的useragent,但是我可以在javascript中执行吗?
答案 0 :(得分:101)
尝试:
navigator.__defineGetter__('userAgent', function(){
return 'foo' // customized user agent
});
navigator.userAgent; // 'foo'
在FF2和FF3中尝试过。
答案 1 :(得分:24)
添加到Crescent Fresh's solution,重新定义navigator.userAgent
getter似乎在Safari 5.0.5中不起作用(在Windows 7和Mac OS X 10.6.7上)。
需要创建一个继承自navigator
对象的新对象,并定义一个新的userAgent
getter来隐藏userAgent
中的原始navigator
getter:
var __originalNavigator = navigator;
navigator = new Object();
navigator.__proto__ = __originalNavigator;
navigator.__defineGetter__('userAgent', function () { return 'Custom'; });
答案 2 :(得分:21)
以下解决方案适用于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');
答案 3 :(得分:7)
使用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,但可以替换整个导航器对象,如上面另一个解决方案所述。
答案 4 :(得分:3)
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'
答案 5 :(得分:3)
要更新此线程,defineGetter在Jasmine中不再使用,因为它已被弃用。但是我发现这允许我修改jasmine中的navigator.userAgent的getter:
navigator = {
get userAgent() {
return 'agent';
}
}
console.log(navigator.userAgent); // returns 'agent'
只要记住在jasmine中完成测试后重置导航器对象
答案 6 :(得分:3)
对于那些试图在TypeScript中做同样事情的人来说,解决方案是:
(<any>navigator)['__defineGetter__']('userAgent', function(){
return 'foo';
});
navigator.userAgent; // 'foo'
语言也是如此:
(<any>navigator)['__defineGetter__']('language', function(){
return 'de-DE';
});
答案 7 :(得分:1)
我想我会采用依赖注入方法。而不是:
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集成。
答案 8 :(得分:1)
对于这里的用户,因为他们需要在单元测试中更改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上为我工作。
答案 9 :(得分:1)
尝试一下,它对我没有毛发问题
Object.defineProperty(global.navigator, 'userAgent', { get: () => 'iPhone' });
答案 10 :(得分:0)
navigator.userAgent
是一个只读字符串属性,因此无法对其进行编辑
答案 11 :(得分:0)
以上答案不适用于PhantomJS + TypeScript。下面的代码对我有用:
var __originalNavigator = navigator;
(window as any).navigator = new Object();
navigator["__proto__"] = __originalNavigator["__proto__"];
navigator["__defineGetter__"]('userAgent', function () { return 'Custom'; });
答案 12 :(得分:0)
该主题仅适用于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
答案 13 :(得分:-1)
通过defineGetter
在Firefox和Opera上更改navigator.userAgentnavigator.__defineGetter__('userAgent', function(){
return( "iPhone 5" );
});
alert( navigator.userAgent ); //iPhone 5
通过对象实例
更改IE和Opera上的navigator.userAgentvar navigator = new Object;
navigator.userAgent = 'iPhone 5';
alert( navigator.userAgent ); //iPhone5
好的是,如果您使用IE webbrowser控件,您可以通过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
答案 14 :(得分:-2)
不,我怀疑你可以在javascript中做到这一点。但是使用Firefox的用户代理切换器,你可以测试你想要的任何用户,那么为什么不用它?