将 react-intl 对象注入到安装的 Enzyme 组件中进行测试
Injecting react-intl object into mounted Enzyme components for testing
编辑: 已解决!向下滚动查看答案
在我们的组件测试中,我们需要它们在没有 <IntlProvider />
父包装器的情况下访问 react-intl
context. The problem is that we are mounting single components (with Enzyme's mount()
)。这可以通过包装提供者来解决,但是 root
指向 IntlProvider
实例而不是 CustomComponent
。
Testing with React-Intl: Enzyme 文档仍然是空的。
class CustomComponent extends Component {
state = {
foo: 'bar'
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
标准测试用例(期望)(酶 + 摩卡 + 柴)
// This is how we mount components normally with Enzyme
const wrapper = mount(
<CustomComponent
params={params}
/>
);
expect( wrapper.state('foo') ).to.equal('bar');
但是,由于我们的组件使用 FormattedMessage
作为 react-intl
库的一部分,所以当 运行 上述代码时,我们会收到此错误:
Uncaught Invariant Violation: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
用IntlProvider
包装它
const wrapper = mount(
<IntlProvider locale="en">
<CustomComponent
params={params}
/>
</IntlProvider>
);
这为 CustomComponent
提供了它要求的 intl
上下文。但是,当尝试进行如下测试断言时:
expect( wrapper.state('foo') ).to.equal('bar');
引发以下异常:
AssertionError: expected undefined to equal ''
这当然是因为它试图读取 IntlProvider
而不是我们的 CustomComponent
的状态。
尝试访问 CustomComponent
我已尝试以下方法无济于事:
const wrapper = mount(
<IntlProvider locale="en">
<CustomComponent
params={params}
/>
</IntlProvider>
);
// Below cases have all individually been tried to call `.state('foo')` on:
// expect( component.state('foo') ).to.equal('bar');
const component = wrapper.childAt(0);
> Error: ReactWrapper::state() can only be called on the root
const component = wrapper.children();
> Error: ReactWrapper::state() can only be called on the root
const component = wrapper.children();
component.root = component;
> TypeError: Cannot read property 'getInstance' of null
问题是:我们如何使用 intl
上下文挂载 CustomComponent
,同时仍然能够对我们的 CustomComponent
执行 "root" 操作?
我创建了一个辅助函数来修补现有的酶 mount()
和 shallow()
函数。我们现在在所有使用 React Intl 组件的测试中使用这些辅助方法。
您可以在这里找到要点:https://gist.github.com/mirague/c05f4da0d781a9b339b501f1d5d33c37
为了保持数据的可访问性,下面是代码的简要说明:
helpers/intl-test.js
/**
* Components using the react-intl module require access to the intl context.
* This is not available when mounting single components in Enzyme.
* These helper functions aim to address that and wrap a valid,
* English-locale intl context around them.
*/
import React from 'react';
import { IntlProvider, intlShape } from 'react-intl';
import { mount, shallow } from 'enzyme';
const messages = require('../locales/en'); // en.json
const intlProvider = new IntlProvider({ locale: 'en', messages }, {});
const { intl } = intlProvider.getChildContext();
/**
* When using React-Intl `injectIntl` on components, props.intl is required.
*/
function nodeWithIntlProp(node) {
return React.cloneElement(node, { intl });
}
export default {
shallowWithIntl(node) {
return shallow(nodeWithIntlProp(node), { context: { intl } });
},
mountWithIntl(node) {
return mount(nodeWithIntlProp(node), {
context: { intl },
childContextTypes: { intl: intlShape }
});
}
};
自定义组件
class CustomComponent extends Component {
state = {
foo: 'bar'
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
CustomComponentTest.js
import { mountWithIntl } from 'helpers/intl-test';
const wrapper = mountWithIntl(
<CustomComponent />
);
expect(wrapper.state('foo')).to.equal('bar'); // OK
expect(wrapper.text()).to.equal('Hello World!'); // OK
编辑: 已解决!向下滚动查看答案
在我们的组件测试中,我们需要它们在没有 <IntlProvider />
父包装器的情况下访问 react-intl
context. The problem is that we are mounting single components (with Enzyme's mount()
)。这可以通过包装提供者来解决,但是 root
指向 IntlProvider
实例而不是 CustomComponent
。
Testing with React-Intl: Enzyme 文档仍然是空的。
class CustomComponent extends Component {
state = {
foo: 'bar'
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
标准测试用例(期望)(酶 + 摩卡 + 柴)
// This is how we mount components normally with Enzyme
const wrapper = mount(
<CustomComponent
params={params}
/>
);
expect( wrapper.state('foo') ).to.equal('bar');
但是,由于我们的组件使用 FormattedMessage
作为 react-intl
库的一部分,所以当 运行 上述代码时,我们会收到此错误:
Uncaught Invariant Violation: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
用IntlProvider
const wrapper = mount(
<IntlProvider locale="en">
<CustomComponent
params={params}
/>
</IntlProvider>
);
这为 CustomComponent
提供了它要求的 intl
上下文。但是,当尝试进行如下测试断言时:
expect( wrapper.state('foo') ).to.equal('bar');
引发以下异常:
AssertionError: expected undefined to equal ''
这当然是因为它试图读取 IntlProvider
而不是我们的 CustomComponent
的状态。
尝试访问 CustomComponent
我已尝试以下方法无济于事:
const wrapper = mount(
<IntlProvider locale="en">
<CustomComponent
params={params}
/>
</IntlProvider>
);
// Below cases have all individually been tried to call `.state('foo')` on:
// expect( component.state('foo') ).to.equal('bar');
const component = wrapper.childAt(0);
> Error: ReactWrapper::state() can only be called on the root
const component = wrapper.children();
> Error: ReactWrapper::state() can only be called on the root
const component = wrapper.children();
component.root = component;
> TypeError: Cannot read property 'getInstance' of null
问题是:我们如何使用 intl
上下文挂载 CustomComponent
,同时仍然能够对我们的 CustomComponent
执行 "root" 操作?
我创建了一个辅助函数来修补现有的酶 mount()
和 shallow()
函数。我们现在在所有使用 React Intl 组件的测试中使用这些辅助方法。
您可以在这里找到要点:https://gist.github.com/mirague/c05f4da0d781a9b339b501f1d5d33c37
为了保持数据的可访问性,下面是代码的简要说明:
helpers/intl-test.js
/**
* Components using the react-intl module require access to the intl context.
* This is not available when mounting single components in Enzyme.
* These helper functions aim to address that and wrap a valid,
* English-locale intl context around them.
*/
import React from 'react';
import { IntlProvider, intlShape } from 'react-intl';
import { mount, shallow } from 'enzyme';
const messages = require('../locales/en'); // en.json
const intlProvider = new IntlProvider({ locale: 'en', messages }, {});
const { intl } = intlProvider.getChildContext();
/**
* When using React-Intl `injectIntl` on components, props.intl is required.
*/
function nodeWithIntlProp(node) {
return React.cloneElement(node, { intl });
}
export default {
shallowWithIntl(node) {
return shallow(nodeWithIntlProp(node), { context: { intl } });
},
mountWithIntl(node) {
return mount(nodeWithIntlProp(node), {
context: { intl },
childContextTypes: { intl: intlShape }
});
}
};
自定义组件
class CustomComponent extends Component {
state = {
foo: 'bar'
}
render() {
return (
<div>
<FormattedMessage id="world.hello" defaultMessage="Hello World!" />
</div>
);
}
}
CustomComponentTest.js
import { mountWithIntl } from 'helpers/intl-test';
const wrapper = mountWithIntl(
<CustomComponent />
);
expect(wrapper.state('foo')).to.equal('bar'); // OK
expect(wrapper.text()).to.equal('Hello World!'); // OK