javascriptember.jsqunitember-qunit

Qunit _initProperties is not a function


I've run into some strange behavior here and wanted to see if anybody knew why it occurs. I've written a simple Ember QUnit test and want to share some data between each test, just to reduce clutter.

Test

import Ember from 'ember'
import { moduleFor, test } from 'ember-qunit';

let create = Ember.Object.create;

let shared = create({});
shared.stardardData1 = create({ id: 1 });
shared.stardardData2 = create({ id: 2 });

moduleFor('controller:foo', 'description', {
    beforeEach() { ... }
    afterEach() { ... }
}

test('should do things', function () {
    let myGroup = [shared.standardData1, shared.standardData2];
}

A couple things here:

I noticed that the QUnit docs say that they've eliminated globals. Could that be playing a role in this? https://qunitjs.com/upgrade-guide-2.x/

PS: It would be nice to have something that set up the module just once instead of every time

Edit

Here's the stacktrace:

Promise rejected before should do things: this._initProperties is not a function
Source:     

    TypeError: this._initProperties is not a function
        at create (http://localhost:7357/assets/vendor.js:46461:14)
        at Object.beforeEach (http://localhost:7357/assets/tests.js:185:20)
        at http://localhost:7357/assets/test-support.js:6586:31
        at tryCatch (http://localhost:7357/assets/vendor.js:61631:14)
        at invokeCallback (http://localhost:7357/assets/vendor.js:61646:15)
        at publish (http://localhost:7357/assets/vendor.js:61614:9)
        at http://localhost:7357/assets/vendor.js:41408:7
        at invoke (http://localhost:7357/assets/vendor.js:11120:16)
        at Object.flush (http://localhost:7357/assets/vendor.js:11184:11)
        at Object.flush (http://localhost:7357/assets/vendor.js:10992:17)
    

Actually I think this might not have to do with the globals. I've also added (was not included before) let create = Ember.Object.create just to save some typing (and wrapped the above objects with the call create(object). Getting rid of that and using the long form seems to get rid of this error though ...


Solution

  • The last part of your question is the actual problem.

    When you do

    let create = Ember.Object.create
    

    You extract the function create from the Ember.Object and create a new reference to it, as a plain function.

    As per JS binding rules, when you call a function as a plain function, this inside of it binds to the global object (or undefined in strict mode). On the other hand, when you call a function as a method on an object (i.e. calling it directly on Ember.Object) would bind this to that object.

    That's why this is undefined within create and hence this._initProperties is not a function.

    Here's a demo of what is happening:

    // an object with a method
    var obj = {
      getThis: function() {
        return this;
      }
    };
    
    // an extracted reference to the method
    var extractedGetThis = obj.getThis;
    
    // this binds to the object
    console.log(
      obj.getThis() === obj // true
    );
    
    // this binds globally
    console.log(
      extractedGetThis() === window // true
    );