【发布时间】:2023-02-09 01:36:07
【问题描述】:
我目前正在为我的一项Odin Project 作业开发一款基于网络的战舰游戏。在某些时候,我觉得中介者模式是处理播放器和 CPU 发射导弹的完美选择。现在,我的任务鼓励我在没有 console.log 但使用 Jest 的情况下彻底测试游戏。我已经能够测试游戏的某些功能,但调解器模式令人困惑。模拟功能或模块可能是正确的方向,但老实说,我已经阅读了大量指南,但我无法实现它们(理解模拟也很困难)。 notifyAttackinside EventManager 函数已经用console.log 以旧方式进行了测试。
谁能让我知道我做错了什么?
事件管理器
export {EventManager}
const EventManager = {
gameManager: GameManager,
notifyAttack(who, coordinate){
if(!who)
throw new Error(`Unknown player`);
else
who === `CPU` ? GameManager.player.board.getAttack(coordinate) : GameManager.cpu.board.getAttack(coordinate);
GameManager.turn = who;
}
}
游戏经理
import {Player} from "./player";
export {GameManager}
const GameManager = {
turn: undefined,
player: undefined,
cpu: Player(),
}
播放器
import {coordinate, GameBoard} from './gameboard';
import { EventManager } from './eventmanager';
export {Player}
const playerActions = {
eventManager: EventManager,
fire(coordinate){
this.eventManager.notifyAttack(this.name, coordinate);
}
}
function Player(name){
const player = Object.create(playerActions);
player.board = GameBoard();
name === undefined ? player.name = `CPU`: player.name = name;
return player;
}
游戏板
import { Ship } from "./ship"
export {GameBoard, coordinate, shipOrientation, tile}
function coordinate(x,y){
const boardSize = 10;
if(x > boardSize || x < 1)
throw new Error(`X coordinate is out of boundaries`);
if(y > boardSize || y < 1)
throw new Error(`Y coordinate is out of boundaries`);
return{x:x, y:y}
}
function tile(coordinate, id){
return{coordinate: coordinate, id: id}
}
const shipOrientation = {
HORIZONTAL: Symbol(`horizontal`),
VERTICAL: Symbol(`vertical`),
}
const gameboardActions = {
placeShips(shipType, orientation, inputCoordinate){
const ship = Ship(shipType);
ship.ID = `${inputCoordinate.x},${inputCoordinate.y}`;
this.tiles.forEach(tile=>{
if(tile.coordinate.x === inputCoordinate.x && tile.coordinate.y === inputCoordinate.y)
throw new Error(`There's already an object on that input coordinate`);
})
if(orientation === shipOrientation.HORIZONTAL){
if(inputCoordinate.x + ship.length > this.size)
throw new Error(`Part of ship is out of board X boundary`);
for(let i = 0; i<ship.length; ++i)
this.tiles.push(tile(coordinate(inputCoordinate.x+i, inputCoordinate.y), `${ship.ID}`));
}else if(orientation === shipOrientation.VERTICAL){
if(inputCoordinate.y + ship.length > this.size)
throw new Error(`Part of ship is out of board Y boundary`);
for(let i = 0; i<ship.length; ++i)
this.tiles.push(tile(coordinate(inputCoordinate.x, inputCoordinate.y+i), `${ship.ID}`));
}else
throw new Error(`Undefined ship orientation`);
this.shipsLog.set(`${ship.ID}`,ship);
},
getAttack(inputCoordinate){
let isShip, ID;
this.tiles.forEach(tile=>{
if(tile.coordinate.y===inputCoordinate.y&&tile.coordinate.x===inputCoordinate.x&&tile.id){
ID = tile.id;
return isShip = true;
}
})
if(isShip){
this.shipsLog.get(ID).hit()
if(this.shipsLog.get(ID).isSunk){
this.removeShip(ID);
this.checkSunkFleet();
}
}else
this.tiles.push(tile(inputCoordinate, undefined));
},
removeShip(ID){
this.shipsLog.delete(ID);
for(let i = 0; i<this.tiles.length; ++i)
if(this.tiles[i].id===ID)
this.tiles.splice(i,1);
},
checkSunkFleet(){
this.shipsLog.size === 0 ? this.sunkFleet=true:this.sunkFleet=false;
}
}
function GameBoard (){
const gameboard = Object.create(gameboardActions);
gameboard.shipsLog = new Map();
gameboard.tiles= [];
gameboard.size= 10;
gameboard.sunkFleet = false;
return gameboard;
}
笑话测试
import {GameBoard, coordinate} from "./gameboard";
import {GameManager} from './gamemanager';
import {Player} from "./player";
import {EventManager} from "./eventmanager";
GameManager.player = Player(`Pablo`);
describe(`Player set up`, ()=>{
test(`Player's name is Pablo`,()=>{
expect(GameManager.player.name).toMatch(/^[A-Z]+$/i);
});
test(`Player has a board to play with`, ()=>{
expect(GameManager.player.board).toMatchObject(GameBoard());
});
})
describe(`Player's actions`,()=>{
test(`Pablo fires a missile, he misses ship target though`, ()=>{
const myCoordinate = coordinate(5,5);
const spy = jest.spyOn(EventManager, 'notifyAttack')
GameManager.player.fire(myCoordinate);
expect(spy).toBeCalled();
expect(GameManager.cpu.getAttack).toBeCalledWith(myCoordinate);
expect(GameManager.cpu.shipsLog.has(`${myCoordinate.x}, ${myCoordinate.y}`));
})
})
所以流程是这样的:
- 已经在
GameManager(Pablo) 中设置的玩家通过触发玩家对象内的fire()发射导弹 -
fire()报告EventManager谁发射了导弹及其坐标 -
EventManager调用CPUGameBoardgetAttack()方法记录Pablo丢失的导弹你们可能想知道为什么我使用
EventManager而不是依赖GameManager。基本上,GameManager负责改变回合,设置游戏,而EventManager专门处理战斗以防止Player和CPU之间的耦合如果您需要有关该问题的更多详细信息,我希望收到您的来信。
干杯!
【问题讨论】:
标签: javascript testing design-patterns jestjs mediator