2017-03-08 79 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'}) 
});