2012-12-04 89 views
3

據我所知,在單元測試methods should be isolated from their dependencies,以便他們不會受到環境變化的影響。單元測試隔離:1行代碼的20行測試?

儘管如此,將所有依賴關係排除在外讓我覺得我正在測試實現而不是行爲。

換句話說,通過隔離依賴關係,我將我的測試與實現細節相結合。因此,即使行爲(期望的結果)沒有改變,任何code refactoring都會導致測試失敗。

例如,這是一個簡單的(紅寶石)方法:

def send_request 
    update_attributes(response.page_params) if active? 
    end 

這些是我的兩個對的這一行代碼分離的測試:

let(:page) { Page.new } 

    describe '#send_request' do 
    context 'when a page is active' do 
     it 'updates page with the response parameters' do 
     page.active = true 
     response = double('response') 
     page_params = double('page_params') 
     response.stub(:page_params).and_return(page_params) 
     page.stub(:response).and_return(response) 
     page.stub(:update_attributes).and_return(nil) 
     page.should_receive(:update_attributes).with(page_params) 
     page.send_request 
     end 
    end 
    context 'when a page is inactive' do 
     it 'does NOT send a request' do 
     page.active = false 
     page.should_not_receive(:response) 
     page.send_request 
     end 
    end 
    end 

的測試都通過,但我看到一些嚴重的問題:

  • 如果後來我決定使用除update_attributes()之外的任何其他方法將更改保存到dat凌辱,我的測試會失敗,即使如預期
  • 如果response.page_params變更的實現,我的軟件會在生產中出現故障,數據將被保存,但測試仍然會通過

我必須做錯事。

編寫單元測試的正確方法是什麼?

+1

我感覺你。不幸的是,沒有「編寫單元測試的正確方法」,就像沒有「銀彈」一樣。一般來說,不錯,根據經驗,與代碼本身相比,您應該花費2倍到3倍的精力編寫適當的單元測試,以達到良好的覆蓋率和信心。另一方面,我用代碼(和測試)的'右手規則'一直是,「我用更少的努力完成更多事情了嗎?」如果不是的話,那麼在某個地方需要解決一個低效率的問題。 –

回答

4

我不認爲你完全不在這裏,因爲AlistairIsrael說。

您可以做一些優化以使其更加簡潔。一個好的測試應該清楚地表明你期望從你的代碼中得到什麼。

let(:page) { Page.new } 

describe '#send_request' do 
    context 'when a page is active' do 
    it 'updates page with the response parameters' do 
     page.active = true 
     response = double('response', 
     :page_params => page_params = mock('page_params') 
    ) 

     # not needed as .should_receive creates a nil stub by default. 
     # page.stub(:update_attributes).and_return(nil) 
     page.should_receive(:update_attributes).with(page_params) 
     page.send_request 
    end 
    end 
    context 'when a page is inactive' do 
    it 'does NOT send a request' do 
     page.active = false 

     subject.should_not_receive(:update_attributes) 
     page.send_request 
    end 
    end 
end 

從上面你一些變化可以看出,rspec的雙人助手是很好很強大,你可以構造複雜的對象,並使用一些分配的東西,你可以有機會獲得最後的評估方法中的鏈條。

我對負面情況做了一個假設,但你應該明白。測試方法調用update_attributes可能更容易,更清晰,因爲你知道page_params永遠不會被調用,如果活動?條件不符合。

HTH

+0

感謝rSpec提示 - 他們真的讓我的測試變得更短,更好。但是,我仍然擔心我的測試有多脆弱。 – krn

+0

如果你擔心測試很脆弱,那麼我會創建真實的對象,並測試這些方法的「後效應」。這有時可能非常乏味(重新創建所有必需的實例等),但使測試非常強大。 – stuartc