Jump to content

Unit testing a Phaser application?


chazzlabs
 Share

Recommended Posts

I'm currently modifying the tutorial project to continue learning Phaser and the basics of game development.  I'm used to using frameworks like AngularJS and Backbone, which are quite easy to unit test.  I've done some searching, but I haven't found any examples of how a unit-tested Phaser application might look.  

 

Are there any suggestions on how one might go about this?  Are there any points or major differences to consider when testing a game versus testing, for example, an Angular or Backbone app?  Does it even make sense to consider adding unit tests?  (Typing the last question made me cringe, but given my success in searching for an example, I felt it necessary.)

Link to comment
Share on other sites

  • 10 months later...
  • 9 months later...

Didn't find much on this topic from searching, so have spent some time today setting up a test bench.

 

My current toolset is:

 

- Mocha

- Chai Assertions

 

Here's a Gist of my current implementation: https://gist.github.com/cloakedninjas/19f0a24b3e249164c113

 

Essentially I init the game into a hidden DIV and allow it to preload. Once the menu state is loaded, the cache is primed and it's safe to begin running tests. I am now able to create individual Game entities and test their interactions, should I need to stub or mock parts of Phaser or my game, I can use Sinon

 

If I run into issues, I will update this post.

Link to comment
Share on other sites

Apps usually wait for some input, process it and return some output or new state. They react in a predictable way. Games are realtime, interactive simulations. You can test if an image processing app returns an image with its colors inverted. As long as the output is correct you don't care whether it took 1 second or 5 depending on the hardware. With a game you have 0.016 seconds to take inputs, process them and present the new state to the player. You may test your collision code and see that it separates the collided objects as intended, but when you run the game the particle emitter makes you skip a frame here and there on lower end hardware and since you used discrete collision detection your sprite now appears to randomly go through walls. 

 

So while you can unit test parts of your game code, I don't know if you'll find someone advocating "test driven game development" as a viable general approach. 

Link to comment
Share on other sites

I actually made a Phaser-powered game that was unit testable. I will try to release the source code in the coming week or so, but there were some other people who worked on it, so I will need to get the OK from them (a couple of people said they may keep working it into a commercial product). 

 

Essentially, I used a pretty radically different pattern for game creation with Phaser that I just off-hand dubbed MSC (Model-State-Controller). I think it would work quite well for a large-scale game, and am actually going to be trying it out fairly soon. The general idea is that you keep all of your data stores in either JS objects or a localStorage database-like system, such as LocalStorageDB (if you are going to have a ton of data) and data-access classes to set and get that data, and your logical objects exist completely outside of Phaser and have no knowledge of Phaser. I chose to use a modified "revealing module pattern" for my classes. Essentially it uses a combination of factory methods and closures to have the ability to create infinite instances, have full inheritance and public and private members that inherit correctly. Here is an example of both a base-class and a child class inheriting from it (by the way, it was a bread-breeding game, so yes, the classes are for breads that breed): 

read.BreadClasses.newBread = function(breadList) {    //private fields    var mature = false;    //public fields    var o = {};    o.breadInfo = breadList;    o.instanceName = "";    /**      @return {bool} - Whether the bread is mature     */    o.isMature = function() {        return mature;    };    /**     @param {string} inName - The instance name for the bread.     */    o.setInstanceName = function(inName) {        this.instanceName = inName;    };    /**     @return {BreadInfo} - The bread data for the bread.     */    o.getBreadInfo = function() {        return this.breadInfo;    };    return o;};

As you can see, it is a function that starts with a JS object and adds members to it, and then returns it, so the function itself is a factory to create as many copies of the object as you need (no "new" used here!). 

 

And here is an example of a class that inherits from it:

/** Factory for the AdultBread class, which inherits from Bread @constructor @type {{AdultBread}} @param {BreadList} breadList - An instance of a BreadList info object. @return {AdultBread} - The new AdultBread instance created. */Bread.BreadClasses.newAdultBread = function(breadList) {    //private fields    var isInBasket = false;    var breedingTimeLeft = 0;    var basketNumber = 0;    var maxBreedingTime = breadList.breedingTime;    //inherit from Bread class and public fields    var o = Bread.BreadClasses.newBread(breadList);    /**     Reduces breeding time by 1     */    o.decrementBreedingTime = function() {        breedingTimeLeft = breedingTimeLeft - 1;    };    /**     * Checks whether the bread is in a breeding basket.     * @return {boolean} - If the bread is in a breeding basket.     */    o.checkIfInBasket = function() {        return isInBasket;    };    /**     * Put the bread in a breading basket.     * @param {boolean} inBasket - Sets whether the bread is in a basket or not.     */    o.setInBasket = function(inBasket) {        isInBasket = inBasket;    };    /**     * Set the basket number for the bread.     * @param {int} basketNum - The basket the bread is in.     */    o.setBasketNumber = function(basketNum) {        basketNumber = basketNum;    };    /**     * Get the breeding basket number of the bread.     * @return {int} - The basket the bread is in.     */    o.getBasketNumber = function() {        return basketNumber;    };    /**     * This method returns whether the bread is growing up and needs to be removed from the collection. Always returns false for adults.     * @returns {boolean}     */    o.isGrowingUp = function() {        return false;;    };    /**     * Sets the current breeding time.     */    o.setBreedTime = function() {        breedingTimeLeft = maxBreedingTime;    };    return o;};

Using this setup, your core game objects exist outside of your game states. Phaser states are then more like the "View" in an MVC application. You create their visual representations inside the state (you can store the Phaser sprite name inside the object for easy reference), and feed them data relates to the changes of the game state, but the core game logic should exist inside your game objects. Only the logic related to displaying the game should be in your state. Of course if you store game data in something like LocalStorageDB, you will have to mock that in your tests.

 

The one place where this code does fall down is that I do not use dependency injection enough, and that ended up making certain things difficult to unit test, so I had to go more with a chaotic integration test for some parts of the application. If anyone uses a setup like this, definitely do not hard-code your base objects when inheriting. Those should be injected. You could inject the factory methods used to create the base class. In fact, that could create some pretty amazing flexibility. If the idea of an entirely dynamic class hierarchy makes you nervous, simply add a "type" member to each object and name them so you can check that the factory passed in was of the type you expected. 

 

For games such as RPGs, strategy games, breeder games, etc., you can pretty much load up all of your logic objects into an HTML page, open the console, and run the entire game from the console command line, getting text feedback, so they are definitely testable. I like using Jasmine, but of course you can use whatever framework you prefer. Action games are a bit more complicated because they rely on the timing that only your objects being run in the state provide. But even in that case you will still be able to unit test the logic of your objects. You will just have to rely a bit more on traditional play testing. 

 

Hope this helps! 

Link to comment
Share on other sites

I would actually keep sprite objects entirely inside the state and simply keep a copy of whatever properties you need for certain logical purposes in the objects. Since Sprite is a Phaser object, you want to try to minimize its connection to the logic objects, and use methods on your objects to set the value so you can set them to whatever you want in the unit tests. This is the strategy I have decided to use for my next attempt with the pattern since writing this code, actually. In this game, the sprite was completely separate from the logic objects since it was a breeder game and there wasn't a ton of logic-based moving around or other such things. I can post some snippets if you like, but I just kept the sprites totally in the state and didn't need to store much of their properties in the object. I would simply make a new sprite if I was making a new bread in the logic objects, for example. 

 

You could do that, or you could choose to keep things like X and Y completely in the state if you only use them for map collisions, etc. Since map collisions is an entirely Phaser-bound process, I don't think it is unit testable (at least without a ton of work). But if you need X and Y for logic that you want to be able to test and for functions that can work entirely outside of Phaser, for example, then you would want to keep a copy in the logic objects and have the state keep them in sync on changes. But if you are going to be using physics or tweens to change the X and Y values, you may have to pass on unit testing them. 

 

Honestly, there is still a lot of unexplored territory and game types with this pattern. I believe I will have much better examples and strategies after I work on my next project using the same pattern, which I plan to be a small RPG. 

Link to comment
Share on other sites

Most of the games I've built with Phaser have been reproductions of board games with an educational edge to them (ex: monopoly, but the chance cards are questions the students answer). With these games; I've unit tested by making sure the Phaser portion of the game represents a strong 'model'. I'll usually divide the model up into the data ("QuestionSet"), and the representation of the game state ("GameBoard", not phaser states; which I'm using less and less).

 

These non-Phaser components can be easily tested with Jasmine and even allow me to TDD them by writing out statements about them ("The board should return a winner"). It then comes down to making sure Phaser represents the game state and not letting game logic slip into Phaser code.

 

When it comes to Phaser itself, I'm not as interested in unit testing, but I find the discussion fascinating. My lack of interest is due to the difficulty testing on all platforms/etc. I mostly user test with "Phaser Inspector" to help. It will be interesting to see what people post in addition to the already good posts.

Link to comment
Share on other sites

  • 4 weeks later...

If you'd like to experiment with End-to-end testing your Phaser games you can try my extension of Nightwatch, which you might find useful: https://www.npmjs.com/package/phase-2-e

 

For unit testing Phaser games I've used Mocha and sinon for stubbing/spying Phaser methods.  You just need to mock your Phaser.Game instance in your tests like:

 

window.game = myGameMock;

 

and myGameMock looks like this:

var sinon = require('sinon');module.exports = {  game: {    physics: {      p2: {        enable: sinon.stub()      }    },    world: {      centerX: 500    },    make: {      sprite: sinon.stub().        returns({          addChild: sinon.stub(),          body: {            clearShapes: sinon.stub(),            addRectangle: sinon.stub(),            setCollisionGroup: sinon.stub(),            collides: sinon.stub()          },          scale: {            setTo: sinon.stub()          },          pivot: {            x: 50,            y: 150          }        })    },    math: {      degToRad: sinon.stub().returns(100)    }  }};
all the methods your game uses on Phaser.Game should be mocked.  

you can then write tests like:

    it("player physics are enabled", function () {      player.init();      expect(game.physics.p2.enable).to.have.been.calledWith(player.sprite);    });

Using Mocha, sinon, chai and sinon-chai

Link to comment
Share on other sites

  • 3 months later...

Nice topic to talk about. I wrote a template JS code for a Phaser 2.2.2 game, but I am not sure how much it is testable. I didn't had that in mind when writing it. 

Here it is: https://github.com/bluePlayer/practices/tree/master/Phaser2Structure

Anyway I am reading a book called: "The Art of unit Testing" by Roy Osherove and it contains lots of useful tips how to write unit tests.  

Since games are state machines I though maybe Model Based Testing is applicable way to test your game? Not sure :)

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...