我正在编写一个Counter组件,其初始状态设置为零,并且从父组件接收到新状态,同时在单元测试中模拟递增或递减函数时,由于“ getderivedstatefromprops”,更改后的状态被父项初始状态覆盖钩。在组件中,它仅被调用一次,但是在单元测试中,当我们在模拟增量函数后更新组件实例时,它会接收初始值并将状态更改为初始状态。 创建组件实例时这有什么方法吗?
export default class Counter extends React.Component<CounterProps,
CounterState> {
state = {
value: 0,
}
static getDerivedStateFromProps(props, state) {
return { ...state, value: props.initialValue }
}
incrementCounter = () => {
const { value } = this.state
const { id, maxValue, countChangeHandler, isDisabled } = this.props
const newValue = value + 1
if (newValue <= maxValue) {
countChangeHandler(id, newValue)
}
this.setState({ value: newValue })
}
render() {
const { value } = this.state
const { maxValue, isDisabled } = this.props
return (
<StyledCounter>
<Count disabled={isDisabled}>{value}</Count>
<Action onClick={this.incrementCounter}>
<Icon shape="plusGray" size="20px" />
</Action>
<Label>{'max ' + maxValue}</Label>
</StyledCounter>
)}
}
单元测试:
describe('Counter components', () => {
function renderCounter(customProperties = {}) {
const properties = {
id: 124355,
initialValue: 3,
maxValue: 5,
isDisabled: false,
countChangeHandler: jest.fn(),
...customProperties,
}
const { output } = renderComponent(<Counter {...properties} />)
return output
}
afterEach(() => {
jest.resetAllMocks()
})
it('On multiple click on Increment button, counter value should not exceed max value.', () => {
const component = renderCounter()
component.find('div#increment').simulate('click')
component.find('div#increment').simulate('click')
component.find('div#increment').simulate('click')
component.find('Counter').update()
expect(component.find(Counter).state('value')).toEqual(5)
})
})
renderComponent正在使用Mount。 单元测试失败 期望5,但收到3。
答案 0 :(得分:0)
因此,当组件本身不更新其某些状态并应依靠父级时,您的组件就像React中的controlled input
模式一样。
我相信您不需要任何特殊技巧就可以对其进行测试。我在这里测试的内容:
props.value
会影响render
的结果props.countChangeHandler
少于props.value + 1
时用props.maxValue
呼叫props.countChangeHandler
props.maxValue
根本不会调用const countChangeHandlerMocked = jest.fn();
function createCounter({ initialValue, maxValue }) {
return shallow(<Counter
initialValue={initialValue}
maxValue={maxValue}
id="mock_id"
countChangeHandler={countChangeHandlerMocked}
/>);
}
it('renders props.value as a Count`, () => {
const wrapper = createCounter({initialValue: 42, maxValue: 100});
expect(wrapper.find(Count).props().children).toEqual(42);
expect(countChangeHandlerMocked).not.toHaveBeenCalled();
});
it('keeps Count in sync with props.value on update', () => {
const wrapper = createCounter({initialValue: 42, maxValue: 100});
wrapper.setProps({ initialValue: 43 });
expect(wrapper.find(Count).props().children).toEqual(43);
expect(countChangeHandlerMocked).not.toHaveBeenCalled();
});
it('reacts on button click if value< maxValue', () => {
const wrapper = createCounter({ initialValue: 1, maxValue: 10 });
wrapper.find(Action).props().onClick();
expect(countChangeHandlerMocked).toHaveBeenCalledWith('mocked_id', 2);
});
it('does not react on button click if value == maxValue', () => {
const wrapper = createCount({ initialValue: 10, maxValue: 10 });
wrapper.find(Action).props().onClick();
expect(countChangeHandlerMocked).not.toHaveBeenCalled();
})
state
看,您越依赖mount()
之类的实现细节或生命周期方法,测试变得越脆弱。像上面说的那样测试,很容易将组件重构为一个功能或多个功能的组合(也许我们需要使用shallow
而不是gDSFP
),并且组件和测试都可以。相反,过分地依赖实现细节会导致您的测试即使在合法更改后仍会失败(例如删除props.initialValue
并使用state.value
代替render()
中的@Override
public ResponseEntity<AppointmentResponse> createAppointment(AppointmentRequest partnerFulfillmentRequest) {
RestTemplate rt = null;
ResponseEntity<AppointmentResponse> response = null;
String uri = null;
HttpEntity<AppointmentRequest> httpEntity = null;
HttpHeaders headers = null;
try {
rt = new RestTemplate();
rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
rt.getMessageConverters().add(new StringHttpMessageConverter());
uri = new String(internalServiceUrl+"/"+APP_NAME_INTERNAL+"/appointment");
log.info("Calling internal service URL : "+uri);
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
httpEntity = new HttpEntity<AppointmentRequest>(partnerFulfillmentRequest, headers);
response = rt.exchange(uri, HttpMethod.PUT, httpEntity, AppointmentResponse.class);
if (response != null)
{
log.info("Got response from internal servicec-->statusCode: "+response.getStatusCodeValue());
log.info("Got response from internal service--> Body "+response.getBody());
}
}catch(HttpClientErrorException hceEx) {
//hceEx.printStackTrace();
AppointmentResponse res = new AppointmentResponse();
return new ResponseEntity<AppointmentResponse>(mapResponse(hceEx.getResponseBodyAsString()), hceEx.getResponseHeaders(), hceEx.getStatusCode());
}catch(Exception e) {
e.printStackTrace();
AppointmentResponse res = new AppointmentResponse();
ResponseEntity<AppointmentResponse> wfmErrResponse = new ResponseEntity<AppointmentResponse>(res, HttpStatus.INTERNAL_SERVER_ERROR);
log.error("ERROR WHILE CALLING INTERNAL SERVICE");
log.error(uri);
log.error(e);
return wfmErrResponse;
}
return response;
}
)。>
这有意义吗?