如何正确单元测试登录和本地存储

时间:2017-05-12 11:14:15

标签: unit-testing reactjs mocha chai enzyme

经过3天的研究而不是在任何地方,我决定向这里询问已经有类似经历或可以指出更好的路径的人。

我发现的更好的问题是这个,但在空中留下了一些问题:React - how to test form submit?

因为我是先生,所以我相信我可能会出错,但不确定是哪一个。如果这是我构建组件的方式,甚至是测试概念本身。

我有以下情况:

  • 当用户登录时,它会调用API(模拟),然后将令牌结果(成功时)保存到localStorage(模拟)
  • 当用户已登录时,会重定向到主页

我的代码到现在为止:

登录组件

class Login extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            password: ''
        };

        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }

    handleSubmit(e) {
        e.preventDefault();
        this.props.sendLoginRequest(this.state).then(
            ({data}) => {
                console.log(data);
            },
            (data) => {
                console.error(data);
            }
        );
    }

    handleChange(e) {
        this.setState({ [e.target.name]: e.target.value });
    }

render() {
        return (
            <div id='auth-container' className='login'>               
                <Form onSubmit={this.handleSubmit}>

                <FormGroup controlId='emailaddress'>
                    <InputGroup bsSize='large'>
                    <InputGroup.Addon>
                        <Icon glyph='icon-fontello-mail' />
                    </InputGroup.Addon>
                    <FormControl 
                        autoFocus
                        className='border-focus-blue'  
                        type='email'                                                     
                        placeholder='email@fixdin.com'
                        name='email'
                        onChange={this.handleChange}
                        value={this.state.email} />
                    </InputGroup>
                </FormGroup>

                <FormGroup controlId='password'>
                    <InputGroup bsSize='large'>
                    <InputGroup.Addon>
                        <Icon glyph='icon-fontello-key' />
                    </InputGroup.Addon>
                    <FormControl 
                        className='border-focus-blue' 
                        type='password' 
                        placeholder='password' 
                        name='password' 
                        onChange={this.handleChange}
                        value={this.state.password} />
                    </InputGroup>
                </FormGroup>
            </Form>
            </div>
        )
    }
}

Login.propTypes = {
    sendLoginRequest: React.PropTypes.func.isRequired
}

authAction.js

import createApi from '../services/api';
import { saveToken } from '../services/session';

export function sendLoginRequest(loginData) {
  return dispatch => {
    const api = createApi();
    const loginPromise = api.post('auth/', loginData);

    loginPromise.then(
      ({ data }) => {
        saveToken(data.token);
      }
    );

    return loginPromise;
  }
}

API..js

import axios from 'axios';
import { isAuthenticated, getToken } from './session';

export const BASE_URL = 'http://localhost:8000/api/v1/';

export default function createAPI() {
    let auth = { }
    if (isAuthenticated()) {
        auth = {
            Token: getToken()
        }
    }

    return axios.create({
       baseURL: BASE_URL,
       auth: auth
    });
};

session.js

const TOKEN_KEY = 'token';

export function saveToken(value)
{
    localStorage.setItem(TOKEN_KEY, value);
}

export function getToken()
{
    return localStorage.getItem(TOKEN_KEY)
}

export function isAuthenticated() {
    return getToken() !== null;
}

我的测试堆栈是Mocha / Chai / Enzyme / sinon,它的定义是

setup.js

var jsdom = require('jsdom');

class LocalStorageMock { 
  constructor() { 
    this.store = {}; 
  } 

  clear() { 
    this.store = {}; 
  } 

  getItem(key) { 
    return this.store[key]; 
  } 

  setItem(key, value) { 
    this.store[key] = value.toString(); 
  } 
}; 

if(!global.document) {
  global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
  global.window = document.defaultView;
  global.navigator = {userAgent: 'node.js'};
  global.localStorage = new LocalStorageMock; 
}

登录-test.js

import React from 'react';
import sinon from 'sinon';
import { mount, shallow } from 'enzyme';
import { expect } from 'chai';
import { Provider } from 'react-redux';
import axios from 'axios'
import moxios from 'moxios'

import store from './../src/store';
import LoginPage from './../src/auth/components/Login';

describe('Login', () => {
    beforeEach(function () {
      moxios.install(axios)
    })

    afterEach(function () {
      moxios.uninstall(axios)
    })

    it('should call action on form submit', () => {
        const submitRequest = sinon.stub(LoginPage.prototype, 'handleSubmit').returns(true);

        const wrapper = mount(<Provider store={store}><LoginPage /></Provider>);
        wrapper.find('form').simulate('submit');
        expect(submitRequest.called).to.be.true;

        submitRequest.restore();
    });

    it('should save token on succesfull login', () => {
        const wrapper = mount(<Provider store={store}><LoginPage /></Provider>);
        const emailInput = wrapper.find('input[type="email"]');
        const passInput =  wrapper.find('input[type="password"]');
        const form = wrapper.find('form');

        emailInput.value = "valid@email.com";
        passInput.value = '123456789';
        form.simulate('submit'); // Should I use submit button instead???

        moxios.wait(function () {
        let request = moxios.requests.mostRecent()

            request.respondWith({
            status: 200,
            response:
                { Token: 'validToken' }          
            }).then(function () {
                expect(localStorage.getItem('Token')).to.equal('validToken');
            });
        });        
    });
});

以上测试未通过,因为它为submitRequest.called返回false,第二次测试失败,错误为"Cannot read property 'respondWith' of undefined"。我不确定如何修复等等,我不确定我是否理想化了它!

在对它进行大量研究时,我已经看到了具有针对组件方法调用+隔离动作测试的测试的示例。 所以......

  1. 当我想到“点击登录并保存令牌”时,我是否过度思考单元测试?有更好的方法来测试这样的东西吗?也许分开一些顾虑?
  2. 这是测试表单提交是否调用其回调的正确方法吗?如果是这样,为什么sinon不在那里工作?
  3. 这是模拟+测试api调用login和localStorage的正确方法吗?如果是这样,为什么Moxios不能正常工作?它不断给我mostRecent()未定义。
  4. 如果不是,问题2和问题3,我在哪里可以找到有关如何正确测试引用行为的有效且有效的例子?
  5. 提前致谢。

0 个答案:

没有答案