Search code examples
typescriptunit-testingmockingmocha.jsscreeps

How do you mock & extend an external defined class and it's prototypes?


Backstory

I'm playing a game called screeps the game allows you to write a bot / ai that performs actions in the game. The way this is accomplished is by uploading code that is run in an environment with available constants, classes and definitions.

Theese definitions are defined with the following package https://www.npmjs.com/package/@types/screeps

Now i'm writing code in typescript and using rollup to compile it for screeps, i'm using the following starter pack https://github.com/screepers/screeps-typescript-starter

I'm attempting to mock with the following framework https://www.npmjs.com/package/@fluffy-spoon/substitute

Problem

When running in mocha, I have to stub / mock the functionality of the environment from the game, it does not exist when running in the test framework. I've managed to mock the constants and have them running in mocha with the help from other people. but now i'm stuck on extending the prototype for the object Creep

prototypes.ts

Object.defineProperty(Creep.prototype, "task", {
      get() {
        ... logic
      },
      set(task: ITask | null) {
        ... logic
      }
    })

test

import "../constants"
import "../../src/tasks/prototypes"
import { Task } from "tasks/Task"
import { Game, Memory } from "./mock"
import { assert } from "chai"
import { Substitute, Arg } from "@fluffy-spoon/substitute"

describe("tasks", () => {
  before(() => {
    // runs before all test in this block
  })

  beforeEach(() => {
    // runs before each test in this block
    // @ts-ignore : allow adding Game to global
    global.Game = _.clone(Game) as Game
    // @ts-ignore : allow adding Memory to global
    global.Memory = _.clone(Memory)
  })

  it("Creep should have extended prototyped", () => {
    const testCreep = Substitute.for<Creep>()

    const task = testCreep.task

    assert.isNotNull(task)

  })
})

When I run the test i'm getting the following error

...\dist\test-unit.bundle.js:3324
}(chai));
^
ReferenceError: Creep is not defined

And that makes sense, because the creep class have not been defined by the game engine or the test environment, so importing prototypes.ts fails at extending it But i'm not sure how to go about actually making it work.


Solution

  • I managed to get it running by doing something along the lines of what I have in the constants definition

    mock.ts

    import { Substitute, Arg } from "@fluffy-spoon/substitute"
    
    export const Game = Substitute.for<Game>()
    
    const mockScreeps = () => {
      const g = global as any
    
      g.Game = Game
      g.Creep = (function() {
        function Creep() {}
    
        return Creep
      })()
    
      g.RoomObject = (function() {
        function RoomObject() {}
    
        return RoomObject
      })()
    
      g.RoomPosition = (function() {
        function RoomObject() {}
    
        return RoomObject
      })()
    }
    
    mockScreeps()
    

    test

    import "../constants"
    import { Memory } from "./mock"
    import { assert } from "chai"
    import "../../src/tasks/prototypes"
    
    describe("tasks", () => {
      before(() => {
        // runs before all test in this block
      })
    
      beforeEach(() => {
        // runs before each test in this block
        // @ts-ignore : allow adding Memory to global
        global.Memory = _.clone(Memory)
      })
    
      it("Creep should have extended prototyped", () => {
        const creep = new Creep("test")
        creep.memory = {
          task: {
            tick: 123,
            name: "dummy"
          }
        }
    
        const task = creep.task
        assert.isNotNull(task)
        if (task && creep.memory.task) {
          assert.isString(task.memory.name)
          assert.equal(task.memory.name, creep.memory.task.name)
        }
    
      })
    })
    

    It now runs my tests, I just need to extend the mocks when making tests that utilizes something that is not mocked yet.