Michael
Michael

Reputation: 4363

Stubbing Meteor `user` and `userId` using `sinon-ts`

I would like to stub user and userId using sinon-ts so I can test my server side code that gets results from the database.

If I use plain sinon I can stub user and userId correctly and the test passes. Although it passes Webstorm shows errors where the returns method doesn't exist etc so I don't think it is playing well with Typescript.

sinon - Passes

import {Factory} from 'meteor/dburles:factory';
import {Meteor} from 'meteor/meteor';
import {resetDatabase} from 'meteor/xolvio:cleaner';
import {GiftListCollectionManager} from "../imports/api/collections/GiftListCollection";
import User = Meteor.User;
import { sinon } from 'meteor/practicalmeteor:sinon';

describe("Test", function () {
    beforeEach(() => {
        resetDatabase();
        Factory.define('user', Meteor.users, {

        });
        currentUser = Factory.create('user');
        sinon.stub(Meteor, 'user');

        Meteor.user.returns(currentUser);

        sinon.stub(Meteor, 'userId');

        Meteor.userId.returns(currentUser._id);
    });

    afterEach(() => {
        Meteor.user.restore();
        Meteor.userId.restore();
        resetDatabase();
    });

    it("Gets giftlists based on Meteor.userId()", () => {
        console.log("Gift lists")
        console.log(GiftListCollectionManager.getInstance().getGiftLists());
    })
}

I decided to give sinon-ts a try so I can get no syntax errors shown. I can't seem to get it to stub user and userId correctly.

sinon-ts - Failing

import {Meteor} from 'meteor/meteor';
import {resetDatabase} from 'meteor/xolvio:cleaner';
import {GiftListCollectionManager} from "../imports/api/collections/GiftListCollection";

import * as sinon from "ts-sinon";

describe("Test", function () {

    let currentUser;

    beforeEach(() => {
        resetDatabase();
        Factory.define('user', Meteor.users, {

        });
        currentUser = Factory.create('user');

        const userStub = sinon.stubObject(Meteor);

        userStub.user.returns(currentUser);

        const userIdStub = sinon.stubObject(Meteor);

        userIdStub.userId.returns(currentUser._id);
    });


    it("Gets giftlists based on Meteor.userId()", () => {
        console.log("Gift lists")
        console.log(GiftListCollectionManager.getInstance().getGiftLists());
    })
});

Error

I20210322-09:45:44.170(0)?      Error: Meteor.userId can only be invoked in method calls or publications.
I20210322-09:45:44.170(0)?       at AccountsServer.userId (packages/accounts-base/accounts_server.js:117:13)
I20210322-09:45:44.171(0)?       at Object.Meteor.userId (packages/accounts-base/accounts_common.js:339:32)
I20210322-09:45:44.171(0)?       at GiftListCollectionManager.getGiftLists (imports/api/collections/GiftListCollection.ts:32:61)
I20210322-09:45:44.171(0)?       at Test.<anonymous> (tests/main.ts:66:61)
I20210322-09:45:44.171(0)?       at run (packages/meteortesting:mocha-core/server.js:36:29)
I20210322-09:45:44.171(0)?       at Context.wrappedFunction (packages/meteortesting:mocha-core/server.js:65:33)

I've spent a lot of time looking around and can't find anything on people stubbing Meteor user and userId using sinon-ts.

What is the correct method of achieving the same results?

Update

Using callsFake throws an exception

import sinon = require('sinon');
sinon.stub(Meteor, 'user').callsFake(() => currentUser);
sinon.stub(Meteor, 'userId').callsFake(() => currentUser._id);

Error

TypeError: sinon.stub(...).callsFake is not a function
at Hook.<anonymous> (tests/main.ts:19:36)
at run (packages/meteortesting:mocha- core/server.js:36:29)

Upvotes: 2

Views: 226

Answers (1)

Jankapunkt
Jankapunkt

Reputation: 8423

It turned out that sinon-ts implements "extensions" around sinon to make it work better with TypeScript but it still supports the "default" sinon behaviour.

The function stubs are known to work with Meteor.user() and Meteor.userId() and can be access via

import sinon, { stubInterface } from "ts-sinon";

const functionStub = sinon.stub();

or via

import * as tsSinon from "ts-sinon"

const functionStub = tsSinon.default.stub();

Applying this scheme to your current code would result in the following code, that is expected to work with Meteor's user functions:

import {Meteor} from 'meteor/meteor';
import * as sinon from "ts-sinon";
// ... other imports

describe("Test", function () {
    let currentUser;

    beforeEach(() => {
        resetDatabase();
        Factory.define('user', Meteor.users, {

        });
        currentUser = Factory.create('user');

        sinon.default.stub(Meteor, 'user').callsFake(() => currentUser);
        sinon.default.stub(Meteor, 'userId').callsFake(() => currentUser._id);
    });

    // ... test units
});

References:

https://github.com/ttarnowski/ts-sinon#sinon-methods

https://sinonjs.org/releases/v9.2.4/stubs/

Upvotes: 2

Related Questions