背景
我正在测试用于替代 Backbone.sync的 Backbone.js 的Backbone.dualStorage插件。
这是一个浏览器插件,但我使用 jasmine-node 来测试它。我将咖啡脚本源加载到节点环境中以window
使用 vm.createContext 模拟浏览器:
vm = require 'vm'
fs = require 'fs'
coffee = require 'coffee-script'
backboneDualstoragePath = './backbone.dualstorage.coffee'
source = fs.readFileSync(backboneDualstoragePath, 'utf8')
window = require('./global_test_context.coffee').window
context = vm.createContext window
coffee.eval source, sandbox: context, filename: backboneDualstoragePath
exports.window = context
这很好用——在我的测试中,我可以访问window.foo
,其中 foo 是在 global_test_context.coffee 中的窗口对象上导出的属性。
我可以对嵌套在窗口沙箱下的任何对象使用 Jasmine 间谍并重新定义方法。例如:
spyOn(window.Store.prototype, 'create').andReturn(true)
window.Store.create() # The spy is called
window.localsync.create() # This function instantiates a Store object and calls create on it
# The spy is called
但是,如果我尝试监视或以其他方式修改上下文的直接属性,则在沙箱上下文中看不到更改:
spyOn(window, 'localsync').andReturn(true)
window.localsync() # The spy is called
window.dualsync() # This function references the global localsync
# The original localsync is called
vm.createContext文档说:
(V8)上下文包括一个全局对象以及一组内置对象和函数。可选参数 initSandbox 将被浅拷贝以播种上下文使用的全局对象的初始内容。
所以听起来它会将我的窗口变量中的属性复制到 vm 上下文中。当我在此之后监视或以其他方式修改window
时,我正在使用 vm 上下文,我将其导出为名为window
. 因此,我认为上面的段落是无关紧要的,但我想包括它以防我错了。
问题
事件流归结为:
window = vm.createContext({globalVariables: forTesting...})
# similiar to vm.runInContext(backboneDualstorageSource, window)
coffee.eval(backboneDualstorageSource, sandbox: window)
# localsync and dualsync are both defined in the backboneDualstorageSource
spyOn(window, 'localsync')
window.dualsync() # calls the original localsync instead of the spy
为什么在 vm 上下文中修改属性后,对 vm 内的那些“全局”属性/函数的引用不会改变?我想明白这一点。
我该如何解决这个问题,以便我可以在我正在测试的浏览器脚本中修改/spyOn 全局变量?
随意查看源代码,以更好地了解与此问题中的片段相比,事物的实际编写方式。
编辑
我能够通过在上下文中运行的 eval 中创建间谍来解决这个问题,就像其他测试代码一样。见https://github.com/nilbus/Backbone.dualStorage/commit/eb6c2b21
有人可以解释为什么我能够在上下文中而不是在上下文之外修改全局变量,即使我可以访问它们?