2017-03-08 35 views
1

這是我寫在Mocha/Chai中的一個測試存根。我可以輕鬆地發出一個動作並聲明狀態與我期望的相同,但我如何驗證它是否遵循了預期的過程(IE早期的測試)?如何單元測試使用還原thunk的還原應用程序

/** 
* This test describes the INITIALIZE_STATE action. 
* The action is asynchronous using the async/await pattern to query 
* The database. The action creator returns a thunk which should in turn 
* return the new state with a list of tables and their relationships with eachother 
**/ 

describe('Initialize state',() => { 
    it('Should check if state is empty',() => {}); 
    it('Should check if tables/relationships exist',() => {}); 
    it('Should check if new tables have been added',() => {}); 
    it('Should merge new and existing tables and relationships',() => { 
     // Here is where we would dispatch the INITIALIZE_STATE 
     // action and assert that the new state is what I expect it to be. 
    }); 
}); 

我還沒有爲實際操作本身編寫任何代碼,因爲我希望代碼能夠通過這些驗證。一些僞代碼可能看起來像這樣

export function initializeState() { 
    return function(dispatch) { 
     let empty = store.getState().empty 
     let state = (empty) ? await getLastPersistedState() : store.getState() 
     let tables = state.tables; 
     let payload = tables.concat(await getNewTables(tables)); 
     dispatch({type: 'INITIALIZE_STATE', payload}); 
    } 
} 

function getLastPerisistedState() { 
    return mongodb.findall(state, (s) => s); 
} 

function getNewTables(tableFilter) { 
    return sql.query("select table_name from tables where table_name not in (" + tableFilter + ")"); 
} 
+0

你想調用實際的mongo/SQL函數,或者模擬它們嗎?我的標準方法是保持異步的東西(這裏是thunk),以便不需要測試,並測試它周圍的所有東西 - 例如用模擬有效載荷測試'INITIALIZE_STATE'動作。 – nrabinowitz

回答

0

這是我提出的解決方案。可能會有更好的一個,但到目前爲止還沒有人能夠提供。我決定採用一套重構的行動和一個獨立的商店來進行我的測試。這些操作是函數發生器而不是使用thunk。他們產生了thunk將在生產代碼中分派的行爲。在我的測試中,我可以自己發送這些動作,並驗證所得到的狀態是我所期望的。這正是thunk所能做的,但它允許我將自己作爲中間人插入,而不是依賴於thunk中間件。

這也是非常有用的,因爲即使在測試異步流程時,它也可以非常容易地將動作邏輯從調度和狀態邏輯中分離出來。

對於數據庫,我自動生成一個存根並使用promise來模擬異步查詢。由於這個項目無論如何都使用了sequelize,所以我只是使用了sequelize來生成存根。

下面是代碼

_actions.js

export function *initializeState() { 
    //We will yield the current step so that we can test it step by step 

    //Should check if state is empty 
    yield {type: 'UPDATE_STEP', payload: 'IS_STATE_EMPTY'}; 
    yield {type: 'UPDATE_STEP_RESULT', payload: stateIsEmpty()}; 

    if(stateIsEmpty()) { 
     //todo: Implement branch logic if state is empty 
    } 
    //... 
} 

sequelize/_test/generate.js

async function createMockFromSql(db, sql, filename) { 
    let results = await db.query(sql, {type: db.Sequelize.QueryTypes.SELECT}); 
    return new Promise((resolve, reject) => { 

     // Trim the results to a reasonable size. Keep it unique each time 
     // for more rigorous testing 

     console.log('trimming result set'); 
     while (results.length > 50) { 
      results.splice(results.length * Math.random() | 0, 1); 
     } 

     fs.writeFile(path.resolve(__dirname, '../../sequelize/_test', filename), JSON.stringify(results, null, 2), err => { 
      if (err) { 
       console.error(err); 
       reject(false); 
      } 

      resolve(true); 
     }) 
    }) 
} 

測試/ actions.js

... 
it('Should check if state is empty',() => { 
    let action = initializeState(); 
    expect(action.next()).to.deep.equal({type: 'UPDATE_STEP', payload: 'IS_STATE_EMPTY'}) 
});