如何在2019年使用redux-mock-store配置或测试容器?

时间:2019-05-20 17:43:33

标签: reactjs redux jestjs enzyme redux-mock-store

我配置了一个容器以使用redux-mock-store测试到最新版本,但出现了一些问题。 find()函数不起作用。我曾经收到零节点和零长度。当我使用mount代替浅函数时,此方法可行,但出现无法识别redux mapDispatchToProps的问题。我如何保证将采取行动?我不想测试商店,但不想测试动作功能,因为我使用thunk。我的推理对吗?

我的容器:

import React, { useState } from 'react'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'

import styles from './Auth.module.css'

import Input from '../../components/UI/Input/Input'
import Button from '../../components/UI/Button/Button'
import Logo from '../../components/UI/Logo/Logo'
import Spinner from '../../components/UI/Spinner/Spinner'
import { auth as authAction } from '../../store/actions/index'
import { checkValidity } from '../../shared/utility'

export const Auth = (props) => {

    const [formIsValid, setFormIsValid] = useState(false)
    const [authForm, setAuthForm] = useState({
        email: {
            elementType: 'input',
            elementConfig: {
                type: 'email',
                placeholder: 'Enter your email'
            },
            value: '',
            validation: {
                required: true,
                isEmail: true
            },
            valid: false,
            touched: false
        },
        password: {
            elementType: 'input',
            elementConfig: {
                type: 'password',
                placeholder: 'Enter your password'
            },
            value: '',
            validation: {
                required: true,
                minLength: 6
            },
            valid: false,
            touched: false
        },
    })

    const inputChangeHandler = (event, controlName) => {
        const updatedControls = {
            ...authForm,
            [controlName]: {
                ...authForm[controlName],
                value: event.target.value,
                valid: checkValidity(event.target.value, authForm[controlName].validation),
                touched: true
            }
        }

        let formIsValid = true;
        for (let inputIdentifier in updatedControls) {
            formIsValid = updatedControls[inputIdentifier].valid && formIsValid
        }

        setAuthForm(updatedControls)
        setFormIsValid(formIsValid)
    }

    const submitHandler = (event, signup) => {
        event.preventDefault()
        props.onAuth(
            authForm.email.value,
            authForm.password.value,
            signup
        )
    }

    const formElementsArray = []
    for (let key in authForm) {
        formElementsArray.push({
            id: key,
            config: authForm[key]
        })
    }

    let formFields = formElementsArray.map(formElement => (
        <Input
            key={formElement.id}
            elementType={formElement.config.elementType}
            elementConfig={formElement.config.elementConfig}
            value={formElement.config.value}
            invalid={!formElement.config.valid}
            shouldValidate={formElement.config.validation}
            touched={formElement.config.touched}
            changed={(event) => inputChangeHandler(event, formElement.id)} />
    ))

    let form = (
        <>
            <form onSubmit={(event) => submitHandler(event, false)}>
                {formFields}
                <Button
                    disabled={!formIsValid}
                    btnType="Default">Log In</Button>
            </form>
            <Button
                clicked={(event) => submitHandler(event, true)}
                disabled={!formIsValid}
                btnType="Link">Sign Up</Button>
        </>
    )
    if (props.loading) {
        form = <Spinner />
    }

    const errorMessage = props.error ? (
        <div>
            <p style={{ color: "red" }}>{props.error}</p>
        </div>
    ) : null;

    let authRedirect = null;
    if (props.isAuthenticated) {
        authRedirect = <Redirect to={'/'} />
    }

    return (
        <main className={styles.Auth}>
            {authRedirect}
            <div className={styles.AuthForm}>
                <h1>Log in to your account</h1>
                <Logo height="3em" />
                {errorMessage}
                {form}
            </div>
        </main>
    )
}

const mapStateToProps = (state) => {
    return {
        loading: state.auth.loading,
        error: state.auth.error,
        isAuthenticated: state.auth.token !== null,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onAuth: (email, password, isSignup) => dispatch(authAction(email, password, isSignup))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Auth)

我的测试:

import React from 'react';
import { Redirect } from 'react-router-dom';
import thunk from 'redux-thunk';

import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import configureStore from 'redux-mock-store';

import Auth from './Auth';
import Spinner from '../../components/UI/Spinner/Spinner';
import Button from '../../components/UI/Button/Button';
import Input from '../../components/UI/Input/Input';

configure({ adapter: new Adapter() });

const setup = () => {
    const props = {
        onAuth: jest.fn()
    }

    const middlewares = [thunk]
    const mockStore = configureStore(middlewares);
    const initialState = {
        auth: {
            token: null,
            email: null,
            error: null,
            loading: false
        }
    };
    const store = mockStore(initialState);

    const enzymeWrapper = shallow(<Auth store={store} {...props} />).dive();

    return {
        enzymeWrapper,
        props,
        store
    }
}

describe('<Auth />', () => {

    it('should calls onSubmit prop function when form is submitted', () => {
        const { enzymeWrapper: wrapper, props: reduxProps, store } = setup();
        const form = wrapper.find('form');

        form.simulate('submit', {
            preventDefault: () => { }
        });
        expect(wrapper.props().onAuth).toHaveBeenCalled();
    });
});

1 个答案:

答案 0 :(得分:1)

要在没有存储连接的情况下测试 const pc = new RTCPeerConnection(configuration) call.onclick = async () => { localVideo.srcObject = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); for (const track of localVideo.srcObject.getTracks()) { pc.addTrack(track, localVideo.srcObject); } }; pc.ontrack = e => remoteVideo.srcObject = e.streams[0]; pc.oniceconnectionstatechange = e => console.log(pc.iceConnectionState); pc.onicecandidate = (e) => { socket.emit('candidate', { candidate: e.candidate }) } pc.onnegotiationneeded = async e => { await pc.setLocalDescription(await pc.createOffer()); socket.emit('sdp', { sdp: pc.localDescription }); } socket.on('data', (data) => { if (data.sdp) { pc.setRemoteDescription(new RTCSessionDescription(data.sdp)); if (data.sdp.type == "offer") { pc.setLocalDescription(pc.createAnswer()); socket.emit('sdp', { sdp: pc.localDescription }) } } else if (data.candidate) { pc.addIceCandidate(new RTCIceCandidate(data.candidate)) } }) 类,您需要使用命名的import而不是默认的import。 PFB该行添加到您的测试文件中以导入Auth组件:

 socket.on('sdp', function(data) {
  socket.broadcast.emit('data',{sdp: data.sdp});
});

socket.on('candidate', function(data) {
  socket.broadcast.emit('data',{candidate: data.candidate});
});

此外,通过这种方法,您无需在渲染时将存储作为道具传递给组件,而可以将动作作为模拟函数传递(您已经在进行Auth动作了)。您也可以通过这种方法使用浅表。