该库是XMLHttpRequest
的模拟,它提供了一个简单的接口来模拟与XMLHttpRequest
交互。它是用于测试的XMLHttpRequest
的直接替代品。
该库实现了XMLHttpRequest
接口,并按照 XMLHTTPRequest 规范中指定的方式处理请求和事件,而无需使用实际的网络请求。您可以通过三种方式响应模拟请求:
您可以使用模拟响应方法来模拟响应、上传进度、错误和其他交互。它们自动处理较低级别的处理,例如发出事件和更改XMLHttpRequest
的readystate
属性。
MockXhr
请求timeout
属性和请求超时MockXhr
生命周期挂钩MockXhrServer
类MockXhrServer
设置MockXhr
类MockXhr
生命周期挂钩MockXhrRequest
类newMockXhr()
newServer()
XMLHttpRequest
特性通过 npm(节点包管理器)
$ npm install mock-xmlhttprequest
import { newServer } from 'mock-xmlhttprequest' ;
import { functionToTest } from '../src/SomethingToTest' ;
// Adapt based on your testing framework. This example uses Mocha and Chai's syntax.
it ( 'should produce a success response' , async ( ) => {
const server = newServer ( {
get : [ '/my/url' , {
// status: 200 is the default
headers : { 'Content-Type' : 'application/json' } ,
body : '{ "message": "Success!" }' ,
} ] ,
} ) ;
try {
// Installs the server's XMLHttpRequest mock in the "global" context.
// After this, "new XMLHttpRequest()" creates a mock request to which the server replies.
server . install ( /* optional context; defaults to globalThis */ ) ;
// Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise
// that resolves to the parsed JSON response
const result = await functionToTest ( ) ;
assert . equal ( result . message , 'Success!' ) ;
} finally {
// Restore the original XMLHttpRequest
server . remove ( ) ;
}
} ) ;
XMLHttpRequest
模拟类是MockXhr
。它公开与XMLHttpRequest
相同的接口,并且是使用XMLHttpRequest
的测试代码的直接替代。
有两个选项可以控制MockXhr
实例的行为:
XMLHttpRequest
生命周期挂钩。如果您需要在没有模拟服务器提供的功能的情况下对请求进行更多控制,请使用此选项。MockXhrServer
类实现模拟服务器。您使用newServer
创建一个MockXhrServer
。 MockXhrServer
自动响应MockXhr
请求并使编写测试变得容易。
使用MockXhrServer
的测试的基本结构是:
import { newServer } from 'mock-xmlhttprequest' ;
const server = newServer ( /* routes */ ) ;
try {
server . install ( /* optional context; defaults to globalThis */ ) ;
// Test your code that creates XMLHttpRequests
} finally {
// Reverts server.install() at the end of the test.
// Only do this after the test case has finished creating XMLHttpRequests.
server . remove ( ) ;
}
有两种方法可以让您的代码使用MockXhr
类来替代XMLHttpRequest
。这允许MockXhrServer
响应请求:
install()
将XMLHttpRequest
类全局替换为服务器的MockXhr
类。在测试用例结束时,调用remove()
以恢复原始状态。XMLHttpRequest
实例的方式,请直接将MockXhr
类与以下MockXhrServer
属性之一结合使用:xhrFactory
是一个创建MockXhr
实例的函数。MockXhr
是xhrFactory
创建的实例的类。此代码演示了xhrFactory
的用法:
import { newServer } from 'mock-xmlhttprequest' ;
const server = newServer ( /* routes */ ) ;
const savedFactory = MyClass . xhrFactory ;
try {
MyClass . xhrFactory = server . xhrFactory ;
// Test code that creates XMLHttpRequests through MyClass.xhrFactory()
} finally {
// Only do this after the test case has finished creating XMLHttpRequests.
MyClass . xhrFactory = savedFactory ;
}
路由定义MockXhrServer
如何响应MockXhr
请求。它们分为三个部分:
当您发送MockXhr
请求时, MockXhrServer
会查找与请求的方法和 URL 匹配的第一个路由。然后它使用路由的请求处理程序进行响应。您还可以设置默认请求处理程序。请求处理程序以声明方式或编程方式定义。
默认情况下,如果请求的timeout
属性设置为非零值并且MockXhrServer
不响应该请求,它最终会超时。
有两种方法可以将路由添加到MockXhrServer
:
newServer
的routes
参数。MockXhrServer
方法。 MockXhrServer
在请求日志中记录它收到的所有MockXhr
请求。使用它来验证您的代码发送的XMLHttpRequest
请求。
MockXhrServer
可以自动生成请求(上传)和响应(下载)进度事件。默认情况下禁用此功能。使用progressRate
字段来启用此功能。
如果您使用Function
类型的请求处理程序以编程方式响应MockXhr
请求,您还可以生成进度事件。
对MockXhr
请求的响应是异步的。这再现了真实的XMLHttpRequest
请求的工作方式。因此,您很可能需要使用测试框架的异步测试支持。例如Mocha测试框架的相关文档在这里。
onSend
生命周期挂钩是响应MockXhr
请求所必需的。模拟服务器会自动处理这个问题。另一种选择是直接使用MockXhr
生命周期挂钩。在这两种情况下, onSend
生命周期挂钩都会在调用XMLHttpRequest.send()
执行上下文完成或清除后执行。在内部,该库使用立即解析的Promise
来获取空的调用堆栈。
MockXhr
请求有几种MockXhr
方法和属性可以响应请求。这些方法允许以下交互:
有关详细信息,请参阅模拟响应方法部分。
timeout
属性和请求超时默认情况下,如果您在代码中设置XMLHttpRequest
的timeout
属性, MockXhr
请求会在指定的延迟后自动超时。这会发出timeout
事件并取消请求,如规范中所述。
依靠时间的流逝来测试代码如何处理超时通常会使测试变得脆弱且难以调试。您可以使用setRequestTimeout()
以编程方式触发超时。
使用以下选项之一禁用自动请求超时:
MockXhrServer
上调用disableTimeout()
。这会影响它处理的所有MockXhr
实例。MockXhr.timeoutEnabled = false
。 MockXhr
类的这个静态属性会影响它的每个实例。MockXhr
实例上将timeoutEnabled
设置为false
。这仅影响该实例。MockXhr
生命周期挂钩这是不使用MockXhrServer
的替代使用模式。您可以直接使用MockXhr
生命周期挂钩。这需要更多代码,但您可以更好地控制MockXhr
请求。
请注意,如果您只需要扩展模拟服务器,您还可以将MockXhr
生命周期挂钩与MockXhrServer
一起使用。
例子:
import { newMockXhr } from 'mock-xmlhttprequest' ;
import { functionToTest } from '../src/SomethingToTest' ;
// Adapt based on your testing framework. This example uses Mocha and Chai's syntax.
it ( 'should produce a success response' , async ( ) => {
// Get a "local" MockXhr subclass
const MockXhr = newMockXhr ( ) ;
// Mock JSON response
MockXhr . onSend = ( request ) => {
const responseHeaders = { 'Content-Type' : 'application/json' } ;
const response = '{ "message": "Success!" }' ;
request . respond ( 200 , responseHeaders , response ) ;
} ;
try {
// Install in the global context so "new XMLHttpRequest()" creates MockXhr instances
global . XMLHttpRequest = MockXhr ;
// Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise
// that resolves to the parsed JSON response
const result = await functionToTest ( ) ;
assert . equal ( result . message , 'Success!' ) ;
} finally {
// Restore the original XMLHttpRequest
delete global . XMLHttpRequest ;
}
} ) ;
MockXhrServer
类此类是一个模拟服务器,它根据 URL 和方法响应MockXhr
请求。
MockXhrServer
设置MockXhrServer(routes)
论据:
routes
:具有服务器初始路由集的对象。 (选修的)在大多数情况下,您应该使用newServer
而不是直接使用此构造函数。
routes
对象的键是 HTTP 方法。这些值是具有两个元素的数组: [url_matcher, request_handler]
。
另请参阅请求 URL 匹配器和请求处理程序。
例子:
const handlerFn = ( request ) => { request . respond ( ) ; } ;
newServer ( {
get : [ '/get' , { status : 200 } ] ,
'my-method' : [ '/my-method' , { status : 201 } ] ,
post : [ '/post' , [ handlerFn , { status : 404 } ] ] ,
} ) ;
install(context = globalThis)
论据:
context
:如果您提供一个值,则install
方法会在此上下文(而不是全局上下文)中设置XMLHttpRequest
属性。 (选修的)在全局上下文中安装服务器的MockXhr
模拟以替换XMLHttpRequest
类。使用remove() 恢复。
remove()
恢复由 install() 所做的更改。测试后调用此方法。
progressRate
如果将progressRate
设置为大于0的number
,服务器会自动生成请求(上传)和响应(下载)进度事件。每个进度事件都会按progressRate
字节递增。
progressRate
仅适用于object
类型的请求处理程序。
disableTimeout()
和enableTimeout()
这些方法禁用或启用MockXhr
的timeout
属性的效果。请参阅“ timeout
属性和请求超时”。
路由配置服务器如何响应MockXhr
请求。下面分别介绍它们的三个部分。
路线概念松散地基于 Express 框架。
允许任何具有有效 HTTP 请求方法的string
。有效方法包括标准方法,例如GET
、 POST
、 PUT
和DELETE
,以及其他方法名称。标准方法名称不区分大小写。
请求 URL 匹配器可以是以下类型之一:
string
(例如'/my-url'
)。RegExp
。true
的Function
。该函数接收 URL 作为参数。 请求处理程序可以是以下类型之一:
具有响应属性的object
。默认值为:
{ status: 200, headers: {}, body: null, statusText: 'OK' }
直接调用模拟响应方法的Function
。该函数接收MockXhrRequest
实例作为参数。
值为'error'
或'timeout'
的string
。这将分别触发错误或超时。
上述其他请求处理程序类型的数组。第一个请求获取第一个处理程序,第二个请求获取第二个处理程序,依此类推。当数组中没有其他处理程序时,将重用最后一个处理程序。
对于object
请求处理程序,服务器会自动添加Content-Length
响应标头和响应正文的长度。
所有这些处理程序都是等效的:
const handlerObj = { } ;
const handlerFn = ( request ) => { request . respond ( 200 , { 'Content-Length' : '0' } ) ; } ;
const handlerArray = [ { } ] ;
get(urlMatcher, handler)
论据:
urlMatcher
:请求 URL 匹配器。handler
:请求处理程序。添加GET
HTTP 方法的路由。
post(urlMatcher, handler)
论据:
urlMatcher
:请求 URL 匹配器。handler
:请求处理程序。添加POST
HTTP 方法的路由。
put(urlMatcher, handler)
论据:
urlMatcher
:请求 URL 匹配器。handler
:请求处理程序。添加PUT
HTTP 方法的路由。
delete(urlMatcher, handler)
论据:
urlMatcher
:请求 URL 匹配器。handler
:请求处理程序。添加DELETE
HTTP 方法的路由。
addHandler(method, urlMatcher, handler)
论据:
method
:作为string
HTTP 方法。urlMatcher
:请求 URL 匹配器。handler
:请求处理程序。添加method
HTTP 方法的路由。
setDefaultHandler(handler)
论据:
handler
:请求处理程序。为不匹配任何路由的请求设置默认请求处理程序。
setDefault404()
设置返回 404 响应的默认请求处理程序。
xhrFactory
返回新MockXhr
实例的函数。
MockXhr
服务器挂钩的MockXhr
类。 xhrFactory
创建此类的实例。
getRequestLog()
返回迄今为止服务器收到的所有请求的数组。每次调用都会返回一个新数组。每个数组元素都是一个具有以下属性的对象:
method
:HTTP 方法string
。url
:URL string
。body
:请求正文headers
:请求标头作为对象。标头名称为小写。MockXhr
类此类是XMLHttpRequest
的模拟。本节记录了规范中未包含的方法和属性。
MockXhr.timeoutEnabled
此静态boolean
属性控制来自类的所有实例的请求的自动超时。
timeoutEnabled
此boolean
属性控制此MockXhr
实例的自动超时。
getResponseHeadersHash()
将所有响应标头作为对象返回。标头名称为小写。
MockXhr
生命周期挂钩您可以在以下位置为MockXhr
生命周期挂钩定义回调方法:
MockXhr
类的静态属性。该钩子适用于MockXhr
及其子类的所有实例。MockXhrServer.MockXhr
或newMockXhr()
返回的MockXhr
子类的静态属性。该钩子适用于该类的所有实例。MockXhr
实例的属性。该钩子仅适用于该实例。如果您为生命周期事件定义多个钩子,则会按上述顺序调用它们。
您通常应该更喜欢第三个选项,它可以更轻松地隔离您的测试用例。
onCreate
接收这些参数的回调方法:
xhr
:新的MockXhr
实例。使用此生命周期挂钩在构造MockXhr
实例时拦截它们。
当创建MockXhr
的实例时,在其构造函数的末尾调用。因此,此生命周期挂钩只能作为静态属性使用。
import { MockXhr , newMockXhr } from 'mock-xmlhttprequest' ;
// Called for all instances of MockXhr and all its subclasses
MockXhr . onCreate = ( xhr ) => { /*...*/ } ;
// Called for all instances of this MockXhr subclass
const MockXhrSubclass = newMockXhr ( ) ;
MockXhrSubclass . onCreate = ( xhr ) => { /*...*/ } ;
onSend
接收这些参数的回调方法:
request
:请求的MockXhrRequest
。xhr
: MockXhr
实例。使用此生命周期挂钩通过模拟响应方法来响应请求。
每次调用send()
后异步调用。每次调用send()
都会生成对onSend
的调用,并使用单独的MockXhrRequest
实例。
import { MockXhr , newMockXhr } from 'mock-xmlhttprequest' ;
// Called for all instances of MockXhr and all its subclasses
MockXhr . onSend = ( request ) => { /*...*/ } ;
// Called for all instances of this MockXhr subclass
const MockXhrSubclass = newMockXhr ( ) ;
MockXhrSubclass . onSend = ( request ) => { /*...*/ } ;
// Called for this instance only
const xhr = new MockXhrSubclass ( ) ;
xhr . onSend = ( request ) => { /*...*/ } ;
MockXhrRequest
类每次调用send()
都会创建一个MockXhrRequest
,其中包含有关XMLHttpRequest
的信息并提供以编程方式响应的方法。
requestHeaders
包含请求标头副本的HeadersContainer
。
method
包含请求的 HTTP 方法的string
。
url
包含请求 URL 的string
。
body
请求的正文。
withCredentials
具有请求的withCredentials
值的boolean
。
getRequestBodySize()
请求正文中的字节number
。
注意:当body
是multipart/form-data
编码的FormData
时,这并不完全准确。不考虑标头、编码和其他影响非模拟XMLHttpRequest
真实body
大小的因素。您可以使用此方法获取请求的真实body
大小的下限值。这对于模拟上传进度事件很有用。
这些方法提供了一个编程接口来响应MockXhr
请求。
如果对响应方法的调用无效,则会抛出Error
并"Mock usage error detected"
的消息。
uploadProgress(transmitted)
论据:
transmitted
:传输的字节number
。触发请求上传进度。
仅当请求body
不为null
且上传未完成时才能调用此方法。
调用此方法后,您可以使用任何其他模拟响应方法。
respond(status = 200, headers = {}, body = null, statusText = 'OK')
论据:
status
:响应HTTP状态number
。 (选修的)headers
:带有响应头的object
。 (选修的)body
:响应主体。 (选修的)statusText
: string
响应 HTTP 状态文本。 (选修的)设置响应标头和正文的完整响应方法。将请求的readyState
更改为DONE
。
触发适当的事件,例如readystatechange
、 progress
和load
。
这是setResponseHeaders()
后跟setResponseBody()
的简写。
调用此方法后,您将无法使用其他模拟响应方法。如果您再次调用open()
则此限制将被解除。
setResponseHeaders(status = 200, headers = {}, statusText = 'OK')
论据:
status
:响应HTTP状态number
。 (选修的)headers
:带有响应头的object
。 (选修的)statusText
: string
响应 HTTP 状态文本。 (选修的)设置响应标头。将请求的readyState
更改为HEADERS_RECEIVED
。
触发适当的事件,例如readystatechange
、 progress
和load
。
调用该方法后,您可以使用以下模拟响应方法:
downloadProgress()
setResponseBody()
setNetworkError()
setRequestTimeout()
。 downloadProgress(transmitted, length)
论据:
transmitted
:传输的字节number
。length
:响应中的字节number
。触发响应进度事件。如果请求的readyState
为HEADERS_RECEIVED
,则将其更改为LOADING
。
您必须在此方法之前调用setResponseHeaders()
。
setResponseBody(body = null)
论据:
body
:响应主体。 (选修的)设置响应正文。将请求的readyState
更改为DONE
。
触发适当的事件,例如readystatechange
、 progress
和load
。
如果尚未调用,则调用setResponseHeaders()
。然后,响应标头仅包含Content-Length
,其值等于响应正文的长度。
调用此方法后,您将无法使用其他模拟响应方法。如果您再次调用open()
则此限制将被解除。
setNetworkError()
模拟网络错误。将请求的readyState
更改为DONE
。
触发适当的事件,包括error
事件。
调用此方法后,您将无法使用其他模拟响应方法。如果您再次调用open()
则此限制将被解除。
setRequestTimeout()
模拟请求超时。将请求的readyState
更改为DONE
。
触发适当的事件,包括timeout
事件。
如果request
属性等于 0,则会引发错误,因为在这种情况下不会发生超时。
调用此方法后,您将无法使用其他模拟响应方法。如果您再次调用open()
则此限制将被解除。
newMockXhr()
返回一个新的MockXhr
子类。
如果您在每个测试用例中使用MockXhr
的不同子类,则更容易确保它们是独立的。例如,如果您在子类上设置timeoutEnabled
静态属性,则它只会影响该子类,而不会影响其他测试用例中创建的其他子类。由于子类不会被重用,因此不需要恢复对子类所做的更改的清理代码。
newServer(routes)
论据:
routes
:具有服务器初始路由集的对象。 (选修的)返回一个新的MockXhrServer
及其自己独特的MockXhr
子类。请参阅newMockXhr()
。
使用可选的routes
参数将路由添加到MockXhrServer
。有关详细信息,请参阅构造函数。
XMLHttpRequest
特性基于 XMLHTTPRequest 规范版本“2022 年 8 月 15 日”。
open()
、 setRequestHeader()
、 send()
和abort()
。statusText
、标头和正文。timeout
属性(可以禁用)。MockXhr.setNetworkError()
)。MockXhr.setRequestTimeout()
)。overrideMimeType()
在需要时抛出,但没有其他效果。responseType
:完全支持''
、 'text'
和'json'
。 responseType
值对传递给setResponseBody()
的响应正文没有影响。responseXml
:响应正文不会转换为文档响应。要获取文档响应,请将其直接作为setResponseBody()
中的响应正文传递。responseUrl
:重定向后的最终请求 URL 不会自动设置。这可以在请求处理程序中模拟。async
在open()
中设置为false
)。open()
中解析请求 URL 并在失败时抛出SyntaxError
。 欢迎贡献者!请参阅本指南了解更多信息。
麻省理工学院