TL; DR Durum bilgisi dahilinde çalışırken otomatik birim testini basitleştirmek için tekniklerin tanımlanmasında yardıma ihtiyacım var.
Arka fon:
Şu anda TypeScript ve Phaser çerçevesinde bir oyun yazıyorum . Phaser, kodunuzun yapısını kısıtlamak için mümkün olduğunca az çalışan bir HTML5 oyun çerçevesi olarak tanımlar. Bu, birkaç değiş tokuş ile geliyor, yani her şeye erişmenizi sağlayan bir Tanrı nesnesi Phaser.Game var: önbellek, fizik, oyun durumları ve daha fazlası.
Bu durum, Tilemap'ım gibi birçok işlevselliği test etmeyi gerçekten zorlaştırıyor. Bir örnek görelim:
Burada fayans katmanlarımın doğru olup olmadığını test ediyorum ve Tilemap'ımdaki duvarları ve yaratıkları tanımlayabiliyorum:
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]
]);
Ne yaparsam yapayım, haritayı oluşturmaya çalıştığımda, Phaser dahili olarak önbelleğini çağırır ve bu sadece çalışma zamanı sırasında doldurulur.
Tüm oyunu yüklemeden bu testi başlatamıyorum.
Karmaşık bir çözüm, haritayı yalnızca ekranda görüntülememiz gerektiğinde oluşturan bir Adaptör veya Proxy yazmak olabilir. Ya da sadece ihtiyacım olan varlıkları manuel olarak yükleyip yalnızca belirli test sınıfı veya modülü için kullanarak oyunu kendim doldurabilirim.
Hissettiğim şeyin daha pragmatik ama yabancı bir çözüm olduğunu seçtim. Oyunumun yüklenmesi ve gerçek oynanışı TestState
arasında, testi tüm varlıklar ve önceden yüklenmiş önbellek verileri ile çalıştıran bir şimşek .
Bu harika, çünkü istediğim tüm işlevselliği test edebiliyorum, ama aynı zamanda soğutmuyorum, çünkü bu teknik bir entegrasyon testi ve sadece ekrana bakıp düşmanların görüntülenip görüntülenmediğini görüp göremeyeceğimi merak ediyor. Aslında hayır, bir Öğe (zaten bir kez oldu) veya daha sonra testlerde - yanlış tanımlanmış olabilirler, ölümlerine bağlı olaylar verilmemiş olabilirler.
Benim sorum - Böyle bir test durumunda daralma yaygın mı? Özellikle JavaScript ortamında bilmediğim daha iyi yaklaşımlar var mı?
Başka bir örnek:
Tamam, neler olduğunu açıklamaya yardımcı olacak daha somut bir örnek:
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);
}
Tilemap'ımı üç bölümden oluşturuyorum:
- Haritalar
key
manifest
Harita gerektirdiği detaylandırma bütün varlıklar (tilesheets ve spritesheets)mapDefinition
Tilemap'in yapısını ve katmanlarını tanımlayan A.
İlk olarak, Phaser içinde Tilemap'i inşa etmek için super'i aramalıyım. Bu, yalnızca içinde tanımlanan anahtarları değil, gerçek varlıkları aramaya çalışırken önbelleğe yapılan tüm çağrıları çağıran bölümdür manifest
.
İkinci olarak, döşeme sayfalarını ve döşeme katmanlarını Tilemap ile ilişkilendiriyorum. Artık haritayı oluşturabilir.
Üçüncüsü, ben yinelerler benim katmanları içinden ve ben haritadan extrude istediğiniz herhangi bir özel nesneleri bulmak: Creatures
, Items
, Interactables
ve benzeri. Bu nesneleri daha sonra kullanmak üzere oluşturur ve saklarım.
Şu anda hala bu varlıkları bulmamı, kaldırmamızı, güncellememi sağlayan nispeten basit bir API'm var:
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 });
}
Kontrol etmek istediğim bu işlevsellik. Döşeme Katmanlarını veya Döşeme Kümelerini eklemezsem, harita görüntülenmez, ancak bunu test edebilirim. Bununla birlikte, super (...) çağrısı bile, testlerimde izole edemediğim bağlama özgü veya durum bilgisi olan mantığı çağırır.
new Tilemap(...)
Phaser'ın önbelleğinde kazmaya başladığım an . Bunu ertelemeliydim, ama bu Tilemap'ımın iki durumda olduğu, biri düzgün şekilde işleyemediği ve tamamen inşa edilmiş olduğu anlamına geliyor.