2017-05-05 170 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',那麼您不會更改該引用內部的內容,而只是替換該引用的值。這有點難以解釋......我希望我有一點道理。 –