Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在函数(类级别)邮戳之外创建的模拟变量上进行Jest测试上下文/间谍_Javascript_Unit Testing_Testing_Mocking_Jestjs - Fatal编程技术网

Javascript 在函数(类级别)邮戳之外创建的模拟变量上进行Jest测试上下文/间谍

Javascript 在函数(类级别)邮戳之外创建的模拟变量上进行Jest测试上下文/间谍,javascript,unit-testing,testing,mocking,jestjs,Javascript,Unit Testing,Testing,Mocking,Jestjs,我试图开玩笑地做一些测试,但却被一个模拟/间谍卡住了。我已经设法让测试工作,但只是通过改变我的实现(这让我感到肮脏) 以下是测试: import * as postmark from 'postmark'; jest.mock('postmark'); const mockGetServers = jest.fn(); const AccountClient = jest.fn(() => { return { getServers: mockGetServers };

我试图开玩笑地做一些测试,但却被一个模拟/间谍卡住了。我已经设法让测试工作,但只是通过改变我的实现(这让我感到肮脏)

以下是测试:

import * as postmark from 'postmark';
jest.mock('postmark');

const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
  return {
    getServers: mockGetServers
  };
});
postmark.AccountClient = AccountClient;

import accountApi from './account-api';

describe('account-api', () => {
  describe('listServers', () => {
    it('calls postmark listServers', async () => {
      await accountApi.listServers();

      expect(mockGetServers).toHaveBeenCalledTimes(1);
    });
  });
});
以下是工作实现:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';

const listServers = async () => {
  try {
    const accountClient = postmark.AccountClient(accountToken);
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}
import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const accountClient = postmark.AccountClient(accountToken);

const listServers = async () => {
  try {
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}
以下是原始实现:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';

const listServers = async () => {
  try {
    const accountClient = postmark.AccountClient(accountToken);
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}
import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const accountClient = postmark.AccountClient(accountToken);

const listServers = async () => {
  try {
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}
唯一的更改是在代码中创建accountClient的位置(在listServers函数内部或外部)。最初的实现将完成,jest将报告没有调用mock

我很困惑,为什么一开始这不起作用,我猜这和模拟的上下文有关。我是不是遗漏了一些笑话在幕后运作的方式?由于accountApi的实现将有更多的函数都使用同一个客户机,因此为所有函数而不是每个函数创建一个函数是有意义的。按函数创建它并不适合我

我创建accountClient的方式有什么不同,这意味着可以在测试中监视模拟?是否有一种方法可以模拟(并监视)在类级别而不是在函数级别创建的对象?


谢谢

我想你可能想看看

注意,我没有测试这个实现;可能需要调整

我是不是遗漏了一些笑话在幕后运作的方式

有两点需要注意:

  • ES6
    import
    调用
  • babeljest
    将调用提升到
    jest.mock
    (首先包括块中的任何ES6
    import
    调用)

  • 我创建accountClient的方式有什么不同,这意味着可以在测试中监视模拟

    在这两种情况下,都会首先运行:

    jest.mock('postmark');
    
    …这将自动模拟
    邮戳
    模块

    然后运行:

    import accountApi from './account-api';
    
    const accountClient = postmark.AccountClient(accountToken);
    const servers = await accountClient.getServers();
    
    在原始实现中此行运行:

    const accountClient = postmark.AccountClient(accountToken);
    
    await accountApi.listServers();
    
    …它捕获调用邮戳.AccountClient的结果,并将其保存在
    AccountClient
    中。
    postmark
    的自动模拟将使用返回
    undefined
    的模拟函数将
    AccountClient
    存根,因此
    AccountClient
    将设置为
    undefined

    在这两种情况下,测试代码现在开始运行,为
    邮戳.AccountClient
    设置模拟

    然后,在测试过程中,该生产线运行:

    const accountClient = postmark.AccountClient(accountToken);
    
    await accountApi.listServers();
    
    原始实现中该调用最终运行以下命令:

    const servers = await accountClient.getServers();
    
    …由于
    accountClient
    未定义,因此将下降到
    catch
    ,并记录错误,测试将继续,直到在此行失败:

    expect(mockGetServers).toHaveBeenCalledTimes(1);
    
    …因为从未调用过
    mockGetServers

    另一方面,在工作实现中运行:

    import accountApi from './account-api';
    
    const accountClient = postmark.AccountClient(accountToken);
    const servers = await accountClient.getServers();
    
    …由于邮戳在这一点上是模拟的,所以它使用模拟并通过测试


    有没有一种方法可以模拟(和监视)在类级别而不是函数级别创建的对象

    因为原始实现会在导入后立即捕获调用
    邮戳.AccountClient
    的结果,所以在导入原始实现之前,您只需确保已设置模拟

    最简单的方法之一是,在调用
    jest.mock
    时,使用a设置mock,因为它首先被提升并运行

    下面是一个更新的测试,可与原始实现一起使用:

    import * as postmark from 'postmark';
    jest.mock('postmark', () => {  // use a module factory
      const mockGetServers = jest.fn();
      const AccountClient = jest.fn(() => {
        return {
          getServers: mockGetServers  // NOTE: this returns the same mockGetServers every time
        };
      });
      return {
        AccountClient
      }
    });
    
    import accountApi from './account-api';
    
    describe('account-api', () => {
      describe('listServers', () => {
        it('calls postmark listServers', async () => {
          await accountApi.listServers();
    
          const mockGetServers = postmark.AccountClient().getServers;  // get mockGetServers
          expect(mockGetServers).toHaveBeenCalledTimes(1);  // Success!
        });
      });
    });
    

    谢谢,有人向我提到过proxyquire,但我认为这个问题不需要进一步的重要回答。现在明白了。谢谢laddo@brian