2017-05-05 103 views
0

我的一个反应成分的测试看起来是这样的(根据关this article):变量范围问题的测试

// MyComponent.test.js 


import { mount } from 'enzyme'; 
import MyComponent from './MyComponent.jsx'; 

describe('<MyComponent />',() => { 
    let props; 
    let state; 
    let mountedComponent; 

    // The problematic part to be changed 
    const component =() => { 
    if (!mountedComponent) { 
     // This enzyme mount is actually much more complex, 
     // as I'm wrapping all sorts of contexts and globals around it 
     // which is why I want to take this function outside, 
     // and use it as boilerplate in every test 
     mountedComponent = mount(<MyComponent {...props} />); 
    } 
    return mountedComponent; 
    }; 

    beforeEach(() => { 
    props = {}; 
    state = {}; 
    mountedComponent = undefined; 
    }); 

    it('Works',() => { 
    state = { val: true }; 
    component().setState(state, 
    () => expect(component().state('val')).to.equal(true), 
    ); 
    }); 
}); 

这个效果很好,component()功能正常返回相同的mountedComponent如果多次调用同样的it,因为mountedComponent的当前值在调用之间被保留,并且只在每次测试之前重置。

现在,如果我提取component()功能本次测试外到另一个文件:

// getMountedComponent.js 

const getMountedComponent = (AnyComponent, props, mountedComponent) => { 

    if (!mountedComponent) { 
    // Appears not to properly reassign mountedComponent 
    mountedComponent = mount(<AnyComponent {...props} />); 
    } 
    return mountedComponent; 
}; 

而更换component()功能与此:

// MyComponent.test.js 

// Cleaner problematic part 
const component =() => getMountedComponent(MyComponent, props, mountedComponent); 

那么这个测试失败,因为成分()第二次返回一个新组件,其中state = null。

它似乎是一个范围问题,但我不能包裹我的头?

回答

1

的问题是,你的getMountedComponent函数接受mountedComponent参数 - 实际上它创建了一个函数内部新mountedComponent变量,它会覆盖在describle块中定义的同名变量。因此,每次您拨打getMountedComponent时,都会创建新的本地变量,因此您从不会为describe范围中定义的变量mountedComponent分配任何值。要解决它,你可以缓存在函数本身(函数在JS第一类对象)组件使用外部变量的insetad:

function getMountedComponent(MyComponent, props) { 

    if (!getMountedComponent.mountedComponent) { 
    // Appears not to properly reassign mountedComponent 
    getMountedComponent.mountedComponent = mount(<MyComponent {...props} />); 
    } 
    return getMountedComponent.mountedComponent; 
}; 

要清除功能的缓存只是使用:

delete getMountedComponent.mountedComponent; 
+0

我从描述函数中抽取函数的原因是因为它实际上比我解释的要复杂一些。这意味着我想'getMountedComponent'生活在另一个文件中。 –

+0

请检查更新的答案。 –

+0

美丽!它的工作:) 我立即用它来写一个函数:'getMountedComponent.reset =()=> {delete getMountedComponent.mountedComponent; }' –

0

Javascript by reference vs. by value

JavaScript是总是通过值传递,但是当一个变量指的是 对象(包括数组)中,“值”是对对象的引用。

当你已经采取了外getMountedComponent功能,你不再设置mountedComponent变量在describe功能,因此它离开undefined,所以它会创建mountedComponent每次的新副本。

const getMountedComponent = (MyComponent, props, mountedComponent) => { 

    if (!mountedComponent) { 
    // You are not changing the "mountedComponent" defined in your "describe" here 
    // because "mountedComponent" is pass by value 
    mountedComponent = mount(<MyComponent {...props} />); 
    } 
    return mountedComponent; 
}; 

describe('<MyComponent />',() => { 
    let props; 
    let state; 
    let mountedComponent; // This stays undefined 
    // ... 
} 
+0

我之所以使用'mountedComponent',可以在'beforeEach'中重置它,因为我实际上有很多测试在这里运行。同意,单个测试是没有用的。 因为mountedComponent最终引用一个React组件,它是一个对象,所以如果再次调用该函数,我期待它通过引用传递,这是不正确的吗? –

+0

@FlorianBienefelt我明白了,我会删除那部分答案。最初,你的变量不是一个对象,所以它将保持不确定。即使有对象,你也只能修改它的属性,所以你不能把它作为一个新的组件来分配。 –

+0

@FlorianBienefelt忽略“最初,你的变量不是一个对象,所以它会保持不确定。”即使有一个对象,也要将引用的值传递给该函数。因此,如果您执行了'mountedComponent = foo',那么您不会更改该引用内部的内容,而只是替换该引用的值。这有点难以解释......我希望我有一点道理。 –