使用 Enzyme 浅层渲染对 Sinon 进行存根
Stubbing with Sinon using Enzyme shallow rendering
我有一个从 JS 组件迁移过来的 React 组件。我正在迁移并检查测试,但我遇到了很多失败,因为存根似乎不再起作用了。这是我的组件...
import DeleteButton from "./delete-button.jsx"
import Dialogs from "../../dialogs";
import React from "react";
import UrlHelper from "../../helpers/url-helper";
export default class ActiveDeleteButton extends React.Component {
/**
* Creates an instance of ActiveDeleteButton.
*
* @param {object} props The react props collection.
*
* @memberOf ActiveDeleteButton
*/
constructor(props) {
super (props);
this.handleConfirmDelete = this.handleConfirmDelete.bind(this);
}
handleConfirmDelete() {
$.ajax({
url: this.props.deleteUri,
type: `DELETE`,
contentType: `application/json; charset=utf-8`,
cache: false,
success: (xhr) => {
let successUri = this.props.successUri;
if (!successUri && xhr && xhr.uri) { successUri = xhr.uri; }
if (successUri) { UrlHelper.redirect(successUri); }
},
error: (xhr, status) => {
this.showFailed();
}
});
}
/**
* Shows failure of deletion.
*
* @memberOf ActiveDeleteButton
*/
showFailed() {
Dialogs.alert(this.props.errorMessage);
}
/**
* Renders the component to the DOM.
*
* @returns the HTML to render.
*
* @memberOf ActiveDeleteButton
*/
render() {
return (
<DeleteButton text = {this.props.text}
title = {this.props.title}
cancelText = {this.props.cancelText}
confirmText = {this.props.confirmText}
message = {this.props.message}
onConfirmDelete = {this.handleConfirmDelete} />
);
}
}
这是测试(浓缩版)...
describe("performs a DELETE AJAX request", () => {
it ("for specified URLs", sinon.test(function() {
let wrapper = shallow(<ActiveDeleteButton text = "Click Me" />);
let instance = wrapper.instance();
let ajaxStub = this.stub($, 'ajax');
instance.forceUpdate()
wrapper.update()
instance.handleConfirmDelete();
console.log(ajaxStub.getCall(0));
let options = ajaxStub.getCall(0).args[0];
assert.equal(options.url, objUt.deleteUri);
assert.equal(options.type, "DELETE");
}));
}));
我遇到的问题是 'ajaxStub.getCall(0)' return 为空。这应该 return Ajax 调用,这样我就可以检查 args(它以前在我的旧 JS 组件中使用过)。存根从未被调用,尽管它(在我看来)显然应该被调用。
我是不是遗漏了什么?
这比其他任何方法都更能解决问题,因此最好能有更好的答案。最后,我为此构建了一个解决方法,如下所示...
首先,我创建了一个新的 class 来处理 Ajax 请求。
/**
* An AJAX request wrapper.
* Usage of this enables testing AJAX calls.
*
* @export AjaxRequest
* @class AjaxRequest
* @extends {AjaxRequest}
*/
export default class AjaxRequest {
/**
* Creates an instance of AjaxRequest.
* @param {any} { url, type, contentType, cache, data, successCallback, errorCallback }
*
* @memberOf AjaxRequest
*/
constructor({ url, type, contentType, cache, data, successCallback, errorCallback }) {
Guard.throwIf(url, "url");
let emptyFunc = () => {};
this.url = url;
this.type = type.toUpperCase() || "GET";
this.contentType = contentType || "application/json; charset=utf-8";
this.dataType = "json";
this.cache = cache || false;
this.data = data ? JSON.stringify(data) : undefined;
this.successCallback = successCallback || emptyFunc;
this.errorCallback = errorCallback || emptyFunc;
}
/**
* Executes the AJAX request.
*
*
* @memberOf AjaxRequest
*/
execute() {
$.ajax({
url: this.url,
type: this.type,
contentType: this.contentType,
dataType: this.dataType,
cache: this.cache,
data: this.data,
success: this.successCallback,
error: this.errorCallback
});
}
/**
* Gets a GET request.
*
* @static
* @param {string} url
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static get(url, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
type: 'GET',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a POST request.
*
* @static
* @param {string} url
* @param {object} data
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static post(url, data, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
data: data,
type: 'POST',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a PUT request.
*
* @static
* @param {string} url
* @param {object} data
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static put(url, data, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
data: data,
type: 'PUT',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a DELETE request.
*
* @static
* @param {string} url
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static delete(url, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
type: 'DELETE',
successCallback: successCallback,
errorCallback: errorCallback
});
}
}
(我觉得这里有一定程度的重新发明)。然后我更新了组件中的方法调用,如下所示...
handleConfirmDelete() {
AjaxRequest.delete(this.props.deleteUri,
(xhr) => {
let successUri = this.props.successUri;
if (!successUri && xhr && xhr.uri) { successUri = xhr.uri; }
if (successUri) { UrlHelper.redirect(successUri); }
},
(xhr, status) => {
this.showFailed();
}
).execute();
}
我现在可以测试如下...
describe("performs a DELETE AJAX request", () => {
let wrapper = null;
let instance = null;
let ajaxStub = null;
let urlHelperRedirectStub = null;
beforeEach(() => {
ajaxStub = sinon.stub(AjaxRequest.prototype, 'execute');
urlHelperRedirectStub = sinon.stub(UrlHelper, 'redirect');
wrapper = shallow(<ActiveDeleteButton text = "Click Me" />);
instance = wrapper.instance();
});
afterEach(() => {
ajaxStub.restore();
urlHelperRedirectStub.restore();
});
it ("for default URLs", sinon.test(function() {
instance.handleConfirmDelete();
sinon.assert.called(ajaxStub);
let requestInfo = ajaxStub.getCall(0).thisValue;
assert.equal(UrlHelper.current.url().split('?')[0], requestInfo.url);
}));
it ("for specified URLs", sinon.test(function() {
wrapper = shallow(<ActiveDeleteButton text = "Click Me" deleteUri="http://localhost/items/12" />);
instance = wrapper.instance();
instance.handleConfirmDelete();
sinon.assert.called(ajaxStub);
let requestInfo = ajaxStub.getCall(0).thisValue;
assert.equal("http://localhost/items/12", requestInfo.url);
}));
}));
使用存根调用的 thisValue
属性 我可以获得回调并手动执行它们以根据不同的输入测试它们。考虑到所需的努力,这不是一个理想的解决方案,但它有效且可重复使用。
不过我觉得一定有更好的方法。
我有一个从 JS 组件迁移过来的 React 组件。我正在迁移并检查测试,但我遇到了很多失败,因为存根似乎不再起作用了。这是我的组件...
import DeleteButton from "./delete-button.jsx"
import Dialogs from "../../dialogs";
import React from "react";
import UrlHelper from "../../helpers/url-helper";
export default class ActiveDeleteButton extends React.Component {
/**
* Creates an instance of ActiveDeleteButton.
*
* @param {object} props The react props collection.
*
* @memberOf ActiveDeleteButton
*/
constructor(props) {
super (props);
this.handleConfirmDelete = this.handleConfirmDelete.bind(this);
}
handleConfirmDelete() {
$.ajax({
url: this.props.deleteUri,
type: `DELETE`,
contentType: `application/json; charset=utf-8`,
cache: false,
success: (xhr) => {
let successUri = this.props.successUri;
if (!successUri && xhr && xhr.uri) { successUri = xhr.uri; }
if (successUri) { UrlHelper.redirect(successUri); }
},
error: (xhr, status) => {
this.showFailed();
}
});
}
/**
* Shows failure of deletion.
*
* @memberOf ActiveDeleteButton
*/
showFailed() {
Dialogs.alert(this.props.errorMessage);
}
/**
* Renders the component to the DOM.
*
* @returns the HTML to render.
*
* @memberOf ActiveDeleteButton
*/
render() {
return (
<DeleteButton text = {this.props.text}
title = {this.props.title}
cancelText = {this.props.cancelText}
confirmText = {this.props.confirmText}
message = {this.props.message}
onConfirmDelete = {this.handleConfirmDelete} />
);
}
}
这是测试(浓缩版)...
describe("performs a DELETE AJAX request", () => {
it ("for specified URLs", sinon.test(function() {
let wrapper = shallow(<ActiveDeleteButton text = "Click Me" />);
let instance = wrapper.instance();
let ajaxStub = this.stub($, 'ajax');
instance.forceUpdate()
wrapper.update()
instance.handleConfirmDelete();
console.log(ajaxStub.getCall(0));
let options = ajaxStub.getCall(0).args[0];
assert.equal(options.url, objUt.deleteUri);
assert.equal(options.type, "DELETE");
}));
}));
我遇到的问题是 'ajaxStub.getCall(0)' return 为空。这应该 return Ajax 调用,这样我就可以检查 args(它以前在我的旧 JS 组件中使用过)。存根从未被调用,尽管它(在我看来)显然应该被调用。
我是不是遗漏了什么?
这比其他任何方法都更能解决问题,因此最好能有更好的答案。最后,我为此构建了一个解决方法,如下所示...
首先,我创建了一个新的 class 来处理 Ajax 请求。
/**
* An AJAX request wrapper.
* Usage of this enables testing AJAX calls.
*
* @export AjaxRequest
* @class AjaxRequest
* @extends {AjaxRequest}
*/
export default class AjaxRequest {
/**
* Creates an instance of AjaxRequest.
* @param {any} { url, type, contentType, cache, data, successCallback, errorCallback }
*
* @memberOf AjaxRequest
*/
constructor({ url, type, contentType, cache, data, successCallback, errorCallback }) {
Guard.throwIf(url, "url");
let emptyFunc = () => {};
this.url = url;
this.type = type.toUpperCase() || "GET";
this.contentType = contentType || "application/json; charset=utf-8";
this.dataType = "json";
this.cache = cache || false;
this.data = data ? JSON.stringify(data) : undefined;
this.successCallback = successCallback || emptyFunc;
this.errorCallback = errorCallback || emptyFunc;
}
/**
* Executes the AJAX request.
*
*
* @memberOf AjaxRequest
*/
execute() {
$.ajax({
url: this.url,
type: this.type,
contentType: this.contentType,
dataType: this.dataType,
cache: this.cache,
data: this.data,
success: this.successCallback,
error: this.errorCallback
});
}
/**
* Gets a GET request.
*
* @static
* @param {string} url
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static get(url, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
type: 'GET',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a POST request.
*
* @static
* @param {string} url
* @param {object} data
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static post(url, data, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
data: data,
type: 'POST',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a PUT request.
*
* @static
* @param {string} url
* @param {object} data
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static put(url, data, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
data: data,
type: 'PUT',
successCallback: successCallback,
errorCallback: errorCallback
});
}
/**
* Gets a DELETE request.
*
* @static
* @param {string} url
* @param {function} successCallback
* @param {function} errorCallback
* @returns an AjaxRequest
*
* @memberOf AjaxRequest
*/
static delete(url, successCallback, errorCallback) {
return new AjaxRequest({
url: url,
type: 'DELETE',
successCallback: successCallback,
errorCallback: errorCallback
});
}
}
(我觉得这里有一定程度的重新发明)。然后我更新了组件中的方法调用,如下所示...
handleConfirmDelete() {
AjaxRequest.delete(this.props.deleteUri,
(xhr) => {
let successUri = this.props.successUri;
if (!successUri && xhr && xhr.uri) { successUri = xhr.uri; }
if (successUri) { UrlHelper.redirect(successUri); }
},
(xhr, status) => {
this.showFailed();
}
).execute();
}
我现在可以测试如下...
describe("performs a DELETE AJAX request", () => {
let wrapper = null;
let instance = null;
let ajaxStub = null;
let urlHelperRedirectStub = null;
beforeEach(() => {
ajaxStub = sinon.stub(AjaxRequest.prototype, 'execute');
urlHelperRedirectStub = sinon.stub(UrlHelper, 'redirect');
wrapper = shallow(<ActiveDeleteButton text = "Click Me" />);
instance = wrapper.instance();
});
afterEach(() => {
ajaxStub.restore();
urlHelperRedirectStub.restore();
});
it ("for default URLs", sinon.test(function() {
instance.handleConfirmDelete();
sinon.assert.called(ajaxStub);
let requestInfo = ajaxStub.getCall(0).thisValue;
assert.equal(UrlHelper.current.url().split('?')[0], requestInfo.url);
}));
it ("for specified URLs", sinon.test(function() {
wrapper = shallow(<ActiveDeleteButton text = "Click Me" deleteUri="http://localhost/items/12" />);
instance = wrapper.instance();
instance.handleConfirmDelete();
sinon.assert.called(ajaxStub);
let requestInfo = ajaxStub.getCall(0).thisValue;
assert.equal("http://localhost/items/12", requestInfo.url);
}));
}));
使用存根调用的 thisValue
属性 我可以获得回调并手动执行它们以根据不同的输入测试它们。考虑到所需的努力,这不是一个理想的解决方案,但它有效且可重复使用。
不过我觉得一定有更好的方法。