skip to Main Content

I’m making a small sized test library, and have a question. How to mock a function defined at the top level? See the full source code in a single file.


function main(){
  var users = getUsers();
  // ...
}

function getUsers() {
  var data = getData()
  // ...
}

function getData() {
  // ... get data from database
}



// test code

function mock_getData() {
  return [{id: 0, name: 'Alice'}, {id: 1, name: 'Bob'}]
}

test('getUsers', function(){
  var users = getUsers()  // HOW TO CALL mock_getData instead of getData in getUsers ?
  myAssert(users[0]['name'], 'Alice')
  myAssert(users[1]['name'], 'Bob')
})

// my very small test library
var tests = []

function myAssert(a, b) {
  if (a !== b) { throw new Error()}
}

function test(name, func) {
  tests.push({name: name, func: func})
}

function runTests() {
  for (i = 0; i < tests.length; i++) {
    var current_test = tests[i]
    try {
      current_test.func()
    } catch (err) {
      console.error(current_test.name)
    }
  }
}

2

Answers


  1. Just use function expressions rather than declarations.

    For example you can set one global var _DEBUG declared somewhere at the beginning of your code like:

    var _DEBUG = false

    then you have something like this:

    // this is function expression so due to hoisting 
    // it won't be defined/loaded until called 
    
    var getData = function() {
       ...get data from database
    }
    

    then somewhere else you can simply do this:

    //keep all your overrides nicely in one place
    
    if(_DEBUG == true){
    
      //override the original function with this one, the good thing is that you keep the name exactly the same 
    
      getData = function(){
         return [{id: 0, name: 'Alice'}, {id: 1, name: 'Bob'}]
      }
    
    }
    
    ///_DEBUG = true //comment-uncomment this to have your test code working
    
    Login or Signup to reply.
  2. Assuming your runtime is browser, toplevel functions sits in the window object.

    //--- main.js
    function getData(){
        return "getData";
    }
    
    //--- test.js
    
    const originalGetData = getData;
    
    function mock(){
        window.getData = function(){ return "mockData"}
    }
    
    function tearDown(){
        window.getData = originalGetData;
    }
    
    function log(){
        console.log(getData(), window.getData(), getData === window.getData);
    }
    
    log(); // getData getData true
    mock();
    log(); // mockData mockData true
    tearDown(); 
    log(); // getData getData true
    

    Same reasoning can be used if the function is not at top level, you will need to alter the given scope/module.

    If your services are classes/scopes, you can also use Proxy.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search