Skip to content

2023 05 23 開發日誌

Hong, Jian-Ching edited this page May 23, 2023 · 4 revisions

概要

ATDD 的 Given 不會寫,不知道測試的「前提」怎麼做

解決:抄作業,實作 DataModel,透過指定的 data 存到 repository 之後就可以正常操作

  /**
   * 3 x 3,只有 1 個地雷,要自己設定地雷位置
   */
  const initData = (): MinesweeperData => {
    const levelConfig = {
      size: {
        x: 3,
        y: 3,
      },
      mineCount: 1,
    };

    const data: MinesweeperData = {
      gameId: randomUUID(),
      gameState: {
        isPlay: true,
        winLose: WinLoseState.NONE,
        displayMineCount: levelConfig.mineCount,
      },
      board: {
        cells: generateCells(levelConfig),
        unopenedCells:
          levelConfig.size.x * levelConfig.size.y - levelConfig.mineCount,
        flagCount: 0,
      },
      levelConfig,
    };

    return data;
  };
  it('踩到地雷遊戲結束', (done) => {
    // Given
    const data = initData();

    // 在 0, 0 放地雷
    data.board.cells[0][0].mine = true;

    const domain: Minesweeper = wsGateway.minesweeperDataModel.toDomain(data);
    wsGateway.minesweeperRepository.save(domain);

    ws.on('open', () => {
      gameInfo(domain.gameId);
    });

    let gameInfoCount = 0;
    ws.on('message', (message) => {
      const event = JSON.parse(message.toString());

      switch (event.event) {
        case 'gameInfo':
          gameInfoCount++;
          switch (gameInfoCount) {
            case 1:
              expect(event.data.gameState.winLose).toBe(WinLoseState.NONE);
              // When
              // 玩家踩地雷
              open(event.data.gameId, 0, 0);
              break;
            case 2:
              // Then
              // 遊戲結束
              expect(event.data.gameState.winLose).toBe(WinLoseState.LOSE);
              done();
              break;
            default:
              throw new Error(`unhandled case`);
          }
          break;
        default:
          throw new Error(`unhandled case`);
      }
    });
  });

WebSocekt 的 switch case 寫法可讀性太差

解決:使用 Promise 裝 WebSocekt Message 包一層

  it('踩到地雷遊戲結束', async () => {
    // Given
    const data = initData();

    // 在 0, 0 放地雷
    data.board.cells[0][0].mine = true;

    const domain: Minesweeper = wsGateway.minesweeperDataModel.toDomain(data);
    wsGateway.minesweeperRepository.save(domain);

    await onWsOpen();
    gameInfo(domain.gameId);

    let event = await onWsMessage();
    expect(event.data.gameState.winLose).toBe(WinLoseState.NONE);

    // When
    // 玩家踩地雷
    open(event.data.gameId, 0, 0);

    // Then
    // 遊戲結束
    event = await onWsMessage();
    expect(event.data.gameState.winLose).toBe(WinLoseState.LOSE);
  });

疑問

  • use case 感覺不出用途 / 實作順序 or 專案太簡單?
    • use case 查改存推
    • 推的 presenter 用途?
  • eventbus 設計用途
  • e2e 如果把 HttpServer 給 mock 掉,這樣的測試 ok 嗎?同理 db 呢?
  • e2e test 在寫的時候,用 data 建立還是 domain 建立
    • 如果用 data 建立,可以模擬很多情況,相對來說有些相聯資料要自己計算
    • 如果用 domain 建立,可以透過原本的邏輯直接跑,但是會特別開功能給測試用,不過理論上 domain 的 unit test 也有這個需求