2016-08-03 14 views
3

我正在寫一個基於Promise的函數的測試。具體而言,它是一個React組件&我正在測試以確保onChange處理程序被正確調用。爲什麼我的Sinon間諜函數在promise條款中調用時不工作?

我的組件看起來是這樣的:

class TextInput extends React.Component { 
    constructor(props) { 
     super(props); 

     this.state = { 
      value: props.value || '', 
     }; 

     this.onChange = this.onChange.bind(this); 
    } 

    updateState(values) { 
     return new Promise(
      (resolve) => { 
       this.setState(values,() => { resolve(this.state); }); 
      } 
     ); 
    } 

    onChange(event) { 
     this.updateState({ value: event.target.value }) 
      // then fire the onChange handler (if necessary) 
      // 
      .then((state) => { 
       if (this.props.onChange) { 
        // console.log(this.props.onChange) shows that this IS a 
        // Sinon spy function 
        this.props.onChange(state.value); 
       } 
      }) 

      .catch((err) => { console.log('-----------', err); }); 
    } 

    render() { 
     // render the component (omitted to keep this short) 
    } 
} 

我的測試是這樣的:在過去兩年expect電話

import React from 'react'; 
import { mount } from 'enzyme'; 
import chai from 'chai'; 
import sinon from 'sinon'; 
import TextInput from '../../../../client/modules/components/TextInput'; 

const expect = chai.expect; 

describe('TextInput component editing',() => { 

    it('calls the onChange handler',() => { 

     const onchange = sinon.spy(); 

     const value = ''; 
     const editedValue = 'something'; 

     const component = mount(<TextInput value={value} onChange={onchange} />); 

     // change the value 
     // 
     component.find('input').simulate('change', { 
      target: { value: editedValue } 
     }); 

     expect(component.find('input').prop('value')).to.equal(editedValue); 

     expect(onchange.calledOnce).to.equal(true); 
     expect(onchange.calledWith(editedValue)).to.equal(true); 
    }); 
}); 

測試失敗。

如果我用簡單的舊函數替換了sinon間諜,函數被調用。例如,

// instead of this... 
// const onchange = sinon.spy(); 

// do this... 
const onchange = (value) => { console.log(`VALUE = ${value}`); }; 

如果我直接使用setState方法的回調,它的工作原理。例如,

// instead of... 
// this.updateState(values).then(...) 

// do this... 
this.setState(values,() => { 
    // call the onChange handler... 
}); 

我能做到這一點,但我想避免它,因爲我要更多的功能添加到這個組件,我不想被困在pyramid of doom

起初我以爲它可能有一些在updateState方法或該方法中的回調函數之一的範圍做一個問題this,但加入console.log陳述整個顯示,this是指的一個實例TextInput在所有適當的地方。

添加一條console.log語句以便在它被觸發之前轉儲onChange處理函數顯示this.props.onChange實際上是一個Sinon間諜。

我看過其他套餐,如sinon-as-promised,但我不認爲這真的包解決什麼,我試圖做的 - 我只是想確保我的回調承諾then子句中調用。 sinon-as-promised是一個包裝整個承諾的軟件包。

我可能忽略了一件簡單的事情,但不管它是什麼,我都沒有看到它。

+1

是不是絕對必要的'this.props.onChange '當狀態發生變化時(而不是當狀態正在被改變時)被調用?問題是你的狀態改變方法是異步的,但你的測試不是(我認爲沒有很乾淨解決方案) – robertklep

+0

感謝@robertklep,你讓我對這個問題有了更多的瞭解,爲此,async並不是必須的,但我想在'setState'上使用回調函數來a)確保改變是在繼續(我承認可能會或可能不會有所作爲)和b)實現語義原因的承諾(多步驟過程中的鏈接步驟並避免厄運的金字塔)。 – Kryten

回答

2

它看起來像你的異步測試完成之前,你的異步調用狀態執行。我不會評論你是否應該既設置狀態,又要求改變方法和時間。但我認爲你目前的簡單答案是通過傳遞done參數來使用異步測試。 (顯然你並不真的需要,即使在這一點上是間諜,但我離開它只是爲了顯示它不是不工作本身的間諜:

describe('TextInput component editing',() => { 
    it('calls the onChange handler', done => { 
    const fakeOnChange = stuff => { 
     expect(spyOnChange.calledOnce).to.equal(true); 
     expect(editedValue).to.equal(stuff); 
     expect(component.find('input').prop('value')).to.equal(editedValue); 
     done(); 
    } 

    const spyOnChange = sinon.spy(fakeOnChange); 

    const value = ''; 
    const editedValue = 'something'; 

    const component = mount(<TextInput value={value} onChange={spyOnChange} />); 

    component.find('input').simulate('change', { 
     target: { value: editedValue } 
    }); 

    }); 
}); 
+1

我正在建議類似的東西。我唯一的問題(但不能很容易解決的問題)是,當fakeOnChange從未被調用(由於組件中的錯誤),測試將超時(這當然表明某事是錯誤的,但是如果事件處理程序被調用或者沒有被調用,能夠明確地測試會更好)。這就是爲什麼我(在我的評論中)詢問async是否絕對有必要開始。 – robertklep

+0

我目前正在使用'setState'上的回調函數,它工作正常。這是我想讓代碼工作的一個例子,而不是過於擔心最優雅的解決方案。但是,感謝您的回答 - 如果我有機會重構此代碼並使其變得可愛,我會做這樣的事情。 – Kryten

相關問題