Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
CraptainRob

Unit testing with GSAP

Recommended Posts

Greetings. A requirement for my current project is that it be unit tested. I'm using TimelineMax for a ton of game code and jasmine.js for unit tests. I've been unit testing by using jasmine's asynchronous tests and allowing timeline to just run normally, but this isn't ideal because it eats so much time. Have any of you found a better way?

 

Link to comment
Share on other sites

I feel your pain. It's a tricky issue to tackle well because there are so many variables involved and the very nature of a tweening engine involves time passing, so you can't just crank through 100 tests in 5 seconds. I don't have a perfect solution for you, but here are some tips:

  1. You can build things with relatively short durations (0.25 seconds or so), but be careful not to go too short because you want to allow some tick events to fire. Not that it'd break things otherwise, but generally when you're testing you want to reproduce something similar to real-world situations. If your tests are artificially short, you might miss something. 
  2. Make use of onComplete/onUpdate/onStart callbacks to run logic that tests properties to make sure they are what they're supposed to be. For example, if you're tweening an element's "left" to 100px, make sure it gets there by the time the onComplete fires. 
  3. You can build out your tests and drop them into a TimelineLite sequence and set its timeScale() to something very high, like 10, so that you don't have to wait a long time. It's easy to drop it back to timeScale of 1 if you want later.

I hope that helps (at least a little). 

Link to comment
Share on other sites

I'm in a similar situation. It could be useful to have a way to inject a simulated clock to drive the greensock Ticker, that lets you say 'x milliseconds passed' so that  greensock reacts using this simulated time.

 

I think it can be done by hand by replacing the _getTime function in TweenLite.js, but would be great if there was out of the box support for this.

Link to comment
Share on other sites

I'm apprehensive about adding that kind of "feature" to the core engine, primarily because we're trying to keep things very tight size-wise and I don't want to add hooks that will bloat the API and potentially allow folks to cause problems with the ticker. However, you could get a similar effect by putting your tweens into a TimelineLite (or just use the TimelineLite.exportRoot() method to grab everything from the root at one time) and then you can have total random access to any spot, thus getting the effect of "x milliseconds passed". Or (as mentioned earlier) set the timeScale() on that timeline to whatever you want (to speed it up or slow it down). 

 

Does that help?

Link to comment
Share on other sites

I did get it to work by just making my tweens short and waiting using setTimeout. To get it to be reliable I had to wait at least an extra frame. I ended up waiting x time + 0.033s. After trying that for a while, I ended up putting all timelines into a factory so I can spy on the methods that create the timeline. Then I spied on TimelineMax.add and watched how the timeline was assembled then I tested each method called by a timeline separately. This way, I don't have to wait for any time to go by and it acts more like how a unit test should. Technically letting GSAP run is more of an integration test anyway.

 

Thanks for the help.

  • Like 1
Link to comment
Share on other sites

  • 2 years later...

I know this is an old thread, but it comes up as the first hit in google when trying to figure out ways to unit test around GSAP.

 

Here is a sample unit test of mine, that I use to test code that uses GSAP:

var $ = require('jquery');
var _ = require('underscore');
var rewire = require('rewire');
var reviewAnimations = rewire('src/animations/reviewAnimations.js');

var stubbedTimeline = {
    play : function() {
    },
    fromTo : function(a, b, c, d) {
        if (_.isEqual(d , {"opacity":1,"scale":1,"ease":"Back.easeOut"})) {
            passed = true;
        }
    }
};

var myTimelineLite = function() {return stubbedTimeline;}

var passed = false;
describe('review animations tester', function() {
    it('set the opacity of the div', function() {
        var div = $('<div style="opacity:0"></div>');
        expect(div.css('opacity')).to.equal('0');
        var restore = reviewAnimations.__set__("TimelineLite", myTimelineLite);

        reviewAnimations.checkmark(div);
        expect(passed).to.equal(true);
        restore();
    });

});

Here I'm testing reviewAnimations.js. What I've done is I replaced TimeLineLite in the file I'm testing with a stubbed out version. If I didn't want to stub every call the original file uses I could have used sinon.stub on a real TimeLineLite before using rewire to inject my fake TimeLineLite into the original file.

 

This works pretty well and has the nice side effect of keeping GSAP from running when my unit tests run.

Thanks,

Joseph Elwell.

Link to comment
Share on other sites

  • 3 years later...

As I keep hitting this question I'll file here my implementation which hopefully can help other folks like me.

 

I use Jest, not only for React but for any project which involves Webpack. Testing with the current version of Jest is super easy.

 

If you read Jest documentation you can simply mock GSAP creating a file in `__mocks__` directory.

 

Let's say you are importing TweenMax and you want to use `to` method:

 

import { TweenMax } from "gsap/TweenMax";

 

Add two files into the __mocks__ directory. TweenLite can be empty.

.
└── __mocks__
    └── gsap
        └── TweenLite.js 
        └── TweenMax.js 

 

module.exports = {
  TweenMax: class{
    static to(selector, time, options) {
      return jest.fn();
    }
  }
}

 

You've successfully mock your `TweenMax.to` method.


Timelines are different

 

Because timeline works on instances of a class the mock should be done this way:

 

import { TimelineMax } from "gsap/TimelineMax";

 

Again add two files into the __mocks__ directory. TweenLite can be empty.

.
└── __mocks__
    └── gsap
        └── TweenLite.js 
        └── TimelineMax.js 

 

module.exports = {
  TimelineMax: class {
    constructor(){
      this.to = jest.fn().mockReturnThis();
    }
  }
};

 

Use `mockReturnThis()` to be able to chain methods.

Edited by La Colonia
Wrong directory structure
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×