TL; DR 상태 저장 프레임 워크 내에서 작업 할 때 자동화 된 단위 테스트를 단순화하는 기술을 식별하는 데 도움이 필요합니다.
배경:
저는 현재 TypeScript 및 Phaser 프레임 워크 에서 게임을 작성하고 있습니다. Phaser는 코드 구조를 제한하기 위해 가능한 한 적은 노력을 기울이는 HTML5 게임 프레임 워크라고 설명합니다. 여기에는 캐시, 물리, 게임 상태 등 모든 것에 액세스 할 수 있는 God-object Phaser.Game 이 있다는 몇 가지 장단점이 있습니다 .
이 상태 저장으로 인해 내 Tilemap과 같은 많은 기능을 테스트하기가 실제로 어려워집니다. 예를 보자.
여기에서는 타일 레이어가 올바르게 설정되어 있는지 테스트하고 타일 맵 내에서 벽과 생물을 식별 할 수 있습니다.
export class TilemapTest extends tsUnit.TestClass {
constructor() {
super();
this.map = this.mapLoader.load("maze", this.manifest, this.mazeMapDefinition);
this.parameterizeUnitTest(this.isWall,
[
[{ x: 0, y: 0 }, true],
[{ x: 1, y: 1 }, false],
[{ x: 1, y: 0 }, true],
[{ x: 0, y: 1 }, true],
[{ x: 2, y: 0 }, false],
[{ x: 1, y: 3 }, false],
[{ x: 6, y: 3 }, false]
]);
this.parameterizeUnitTest(this.isCreature,
[
[{ x: 0, y: 0 }, false],
[{ x: 2, y: 0 }, false],
[{ x: 1, y: 3 }, true],
[{ x: 4, y: 1 }, false],
[{ x: 8, y: 1 }, true],
[{ x: 11, y: 2 }, false],
[{ x: 6, y: 3 }, false]
]);
내가 무엇을하든 맵을 만들려고하자마자 Phaser는 내부적으로 캐시를 호출하며 런타임 중에 만 채워집니다.
전체 게임을로드하지 않으면이 테스트를 호출 할 수 없습니다.
복잡한 해결책은 화면에지도를 표시해야 할 때만지도를 작성하는 어댑터 또는 프록시를 작성하는 것입니다. 또는 필요한 자산 만 수동으로로드 한 다음 특정 테스트 클래스 또는 모듈에 대해서만 사용하여 게임을 직접 채울 수 있습니다.
나는 더 실용적이라고 생각하지만 이것에 대한 외국 솔루션을 선택했습니다. 내 게임 로딩과 실제 재생 사이에 TestState
이미로드 된 모든 자산과 캐시 된 데이터로 테스트를 실행 하는를 시작했습니다 .
이것은 내가 원하는 모든 기능을 테스트 할 수 있기 때문에 멋지다. 또한 기술적 인 통합 테스트이기 때문에 화면을보고 적을 표시 할 수 없는지 의아해하기 때문이다. 실제로, 아니, 그들은 시험에서 한 항목 (이미 한 번 일어난)으로 잘못 인식되었을 수도 있고 나중에 시험에서 그들의 죽음과 관련된 사건을받지 않았을 수도 있습니다.
내 질문 -shimming이 이와 같은 테스트 상태입니까? 특히 JavaScript 환경에서 잘 모르는 더 나은 접근 방법이 있습니까?
또 다른 예:
다음은 현재 상황을 설명하는 데 도움이되는보다 구체적인 예입니다.
export class Tilemap extends Phaser.Tilemap {
// layers is already defined in Phaser.Tilemap, so we use tilemapLayers instead.
private tilemapLayers: TilemapLayers = {};
// A TileMap can have any number of layers, but
// we're only concerned about the existence of two.
// The collidables layer has the information about where
// a Player or Enemy can move to, and where he cannot.
private CollidablesLayer = "Collidables";
// Triggers are map events, anything from loading
// an item, enemy, or object, to triggers that are activated
// when the player moves toward it.
private TriggersLayer = "Triggers";
private items: Array<Phaser.Sprite> = [];
private creatures: Array<Phaser.Sprite> = [];
private interactables: Array<ActivatableObject> = [];
private triggers: Array<Trigger> = [];
constructor(json: TilemapData) {
// First
super(json.game, json.key);
// Second
json.tilesets.forEach((tileset) => this.addTilesetImage(tileset.name, tileset.key), this);
json.tileLayers.forEach((layer) => {
this.tilemapLayers[layer.name] = this.createLayer(layer.name);
}, this);
// Third
this.identifyTriggers();
this.tilemapLayers[this.CollidablesLayer].resizeWorld();
this.setCollisionBetween(1, 2, true, this.CollidablesLayer);
}
세 부분으로 타일 맵을 만듭니다.
- 지도
key
manifest
지도에 필요한 상세하게 모든 자산 (tilesheets 및 spritesheets)mapDefinition
tilemap의 층 구조를 설명한다.
먼저 Phaser 내에서 Tilemap을 구성하려면 super를 호출해야합니다. 이것은에 정의 된 키뿐만 아니라 실제 자산을 검색하려고 할 때 캐시에 대한 모든 호출을 호출하는 부분입니다 manifest
.
둘째, 타일 시트와 타일 레이어를 타일 맵과 연결합니다. 이제 맵을 렌더링 할 수 있습니다.
셋째, 나는 반복 처리 내 레이어를 통해 내가지도에서 밀어 내기를 원하는 특별한 개체를 찾을 : Creatures
, Items
, Interactables
등. 나중에 사용할 수 있도록 이러한 개체를 만들어 저장합니다.
나는 여전히이 엔티티를 찾고 제거하고 업데이트 할 수있는 비교적 간단한 API를 여전히 가지고 있습니다.
wallAt(at: TileCoordinates) {
var tile = this.getTile(at.x, at.y, this.CollidablesLayer);
return tile && tile.index != 0;
}
itemAt(at: TileCoordinates) {
return _.find(this.items, (item: Phaser.Sprite) => _.isEqual(this.toTileCoordinates(item), at));
}
interactableAt(at: TileCoordinates) {
return _.find(this.interactables, (object: ActivatableObject) => _.isEqual(this.toTileCoordinates(object), at));
}
creatureAt(at: TileCoordinates) {
return _.find(this.creatures, (creature: Phaser.Sprite) => _.isEqual(this.toTileCoordinates(creature), at));
}
triggerAt(at: TileCoordinates) {
return _.find(this.triggers, (trigger: Trigger) => _.isEqual(this.toTileCoordinates(trigger), at));
}
getTrigger(name: string) {
return _.find(this.triggers, { name: name });
}
확인하고 싶은 기능입니다. 타일 레이어 또는 타일 세트를 추가하지 않으면 맵이 렌더링되지 않지만 테스트 할 수 있습니다. 그러나 super (...) 호출조차도 테스트에서 분리 할 수없는 컨텍스트 특정 또는 상태 저장 논리를 호출합니다.
new Tilemap(...)
Phaser가 캐시에서 파기 시작 하는 순간 . 나는 그것을 연기해야하지만, 내 Tilemap은 두 가지 상태, 즉 제대로 렌더링 할 수없는 상태와 완전히 구성된 상태라는 것을 의미합니다.