sinonjs / sinon

Test spies, stubs and mocks for JavaScript.
https://sinonjs.org/
Other
9.63k stars 770 forks source link

Click Event fired multiple Times #282

Closed FoxGit closed 11 years ago

FoxGit commented 11 years ago

Hi everyone,

my Setup is as follows (and I hope this is the right place to post): I use EmberJS and some other Frameworks in my App and MochaJS, ChaiJS and SinonJS together with grunt-mocha for testing. I am trying to test some views, especially clicks but can't get it right.

describe("BereichHauptmenueView", function() {

        var controller, view;
        before(function() {        
            Ember.run(function() {
                //setting up stuff           
            });            
        });

        after(function() {                
            //removing stuff
        });

        beforeEach(function () {            
            Ember.run(function () {
                App.set('currentStatus', 'ONLINE');              
                //setting up more stuff               
                controller = Ember.Controller.create();
                view = App.BereichHauptmenueView.create({
                    controller: controller
                });                   
                //Sinon stubs für die View Funktionen
                sinon.spy(view, "redirect");                
                // Hook up to our document.
                view.append();
            });
        });

        afterEach(function () {            
            Ember.run(function () {            
                view.redirect.restore();
                // Unhook from our document.
                view.remove(); 
            });
        });
        describe("someThing", function() {
                it("someTest", function () {
                    view.$(".someDiv").click();
                    view.redirect.should.have.been.calledOnce;
                });
         });
});

But I get: AssertionError: expected redirect to have been called exactly once, but it was called 13 times.

The number of called times is the position of the test in my suits, which is weird, because some tests do not even contain a view. DO I have to pay attention to listeners being added again and again? As far as I can see, there is no way I have multiple views in the dom. Thanks for any tips.

mantoni commented 11 years ago

The first thing I noticed is that view.$(".someDiv").click(); triggers an asynchronous execution. You will need to find a way to trigger the click handler synchronously.

Then, you don't care about calls to redirect during setup. Instead of the spy setup in beforeEach you could use a sandbox to only stub it away:

it("someTest", sinon.test(function () {
  this.stub(view, "redirect");

  view.$(".someDiv").click();

  view.redirect.should.have.been.calledOnce;
}));

Alternatively, you could also just reset the spy and keep the setup in beforeEach:

it("someTest", function () {
  view.redirect.reset(); // remove all recorded calls from spy

  view.$(".someDiv").click();

  view.redirect.should.have.been.calledOnce;
});

However, as long as the click() call triggers a DOM event, it will probably fail saying that view.redirect was called 0 times.

One other question that might be related: Does Ember.run execute asynchronously?

FoxGit commented 11 years ago

Thx for your answer.

this.stub(view, "redirect");

results in a

"TypeError: Object #<Context> has no method 'stub'" 

error message. However

sinon.stub(view, "redirect");

works, but is called numerous times as well. The spy reset approach sadly gives the same results. :( The function is called several times. Even if I define the spy in the test itself, the function gets called several times. When searching via jQuery only one dom element of "someDiv" is found. I get the feeling there may be several click listeners to it attached somehow... As far as I understood it Ember.run is not executed asynchronously. I am however not an Expert for the Run Loop. I will try to gather a deeper understanding of it to find clues. In the meantime I am grateful for more tips. :)

mantoni commented 11 years ago

this.stub(obj, 'method') requires the test function to be wrapped with sinon.test(...) - see my first snippet above.

FoxGit commented 11 years ago

Yes, my bad, sorry. It works that way but the function is still called several times :(

edit On further investment it seems that each time

App.reset() 

is called a new event listener is registered. That explains why the amount of clicks is equal to the number of tests as App.reset() is performed after each test. I have no idea however why there should be additional event listeners, I am not even sure there are any. I don't see any using chrome developer tools.

edit2 After updating ember to the latest master, the problem is solved. As to the possible numerous changes in the different master versions to come I will disable the affected tests for now and wait for the next RC. Thx @mantoni for your assistance in this matter.

mantoni commented 11 years ago

You're welcome. In case you find a working solution on how to use Sinon with Ember, I'd love to see a short example on the Wiki.