2013-03-04 52 views
1

我第一次嘗試SpecFlow,我想知道我是否可能推翻了整個概念或者更糟糕,完全濫用了它的預期目的?使用SpecFlow進行項目體系結構(MVP-VM)

我想爲我的WinForms項目採用MVP-VM體系結構設計模式,並列出了將按照相同模式定義未來項目的樣板故事。

歡迎任何建議,謝謝!

Feature: DesignPattern 
    In order to encourage pluggability and loose coupling 
    As a software developer who has to comply with company GUI standards 
    I want to make sure the MVP-VM design pattern is enforced 

@mytag 
Scenario: MainPresenter loosely couples with IMainView and IModelContainer implementations 
    Given a stub of the IMainView interface 
    And a stub of the IModelContainer interface 
    When I create a new MainPresenter with the IMainView and IModelContainer stubs as arguments 
    Then the MainPresenter should have the IMainView and IModelContainer stubs as properties 

Scenario: MainPresenter tightly couples with MainViewModel 
    Given a stub of the IMainView interface 
    And a stub of the IModelContainer interface 
    When I create a new MainPresenter with the IMainView and IModelContainer stubs as arguments 
    Then the MainPresenter should have a collection of MainViewModels as a property 

Scenario: IModelContainer contains all required model interfaces 
    Given a stub of the IModelContainer interface 
    Then the IModelContainer stub should have an IContractsModel property 

Scenario: IMainView extends the company BaseView GUI standard 
    Given a stub of the IMainView interface 
    Then the IMainView stub should extend the IBaseView interface 

Scenario: IMainView exposes a datasource binding method that accepts a collection of MainViewModels as argument 
    Given a stub of the IMainView interface 
    And a collection of MainViewModels 
    Then the IMainView stub should have a BindViewModelsList method that accepts the collection of MainViewModels 

Scenario: MainViewModel takes a ContractDataEntity and stores it as a property 
    Given a ContractDataEntity 
    When I create a new MainViewModel with the ContractDataEntity as argument 
    Then the MainViewModel should have the ContractDataEntity as a property 

Scenario: MainViewModel presents the required attributes of its associated DataEntity 
    Given a ContractDataEntity 
    When I create a new MainViewModel with the ContractDataEntity as argument 
    Then the MainViewModel should have the ContractDataEntity ContractNumber as a property 
    And the MainViewModel should have the ContractDataEntity CustomerCode as a property 

Scenario: MainViewModel has a factory method that translates a collection of DataEntities into MainViewModels 
    Given a collection of ContractDataEntities 
    When I call the MainViewModel TranslateDataEntityList factory method 
    Then it should return a collection on MainViewModels 

我會再生成類,屬性和方法存根從SpecFlow方法:

using Rhino.Mocks; 
using Should.Fluent; 
using TechTalk.SpecFlow; 

namespace CONTR001.Test 
{ 
    [Binding] 
    public class DesignPatternSteps 
    { 
     [Given(@"a stub of the IMainView interface")] 
     public void GivenAStubOfTheIMainViewInterface() 
     { 
      IMainView view = MockRepository.GenerateStub<IMainView>(); 
      ScenarioContext.Current.Set(view); 
     } 

     [Given(@"a stub of the IModelContainer interface")] 
     public void GivenAStubOfTheIModelContainerInterface() 
     { 
      IModelContainer model = MockRepository.GenerateStub<IModelContainer>(); 
      ScenarioContext.Current.Set(model); 
     } 

... 

     [When(@"I create a new MainPresenter with the IMainView and IModelContainer stubs as arguments")] 
     public void WhenICreateANewMainPresenterWithTheIMainViewAndIModelContainerStubsAsArguments() 
     { 
      var view = ScenarioContext.Current.Get<IMainView>(); 
      var model = ScenarioContext.Current.Get<IModelContainer>(); 
      var presenter = new MainPresenter(view, model); 
      ScenarioContext.Current.Set(presenter); 
     } 

... 

     [Then(@"the MainPresenter should have the IMainView and IModelContainer stubs as properties")] 
     public void ThenTheMainPresenterShouldHaveTheIMainViewAndIModelContainerStubsAsProperties() 
     { 
      var presenter = ScenarioContext.Current.Get<MainPresenter>(); 
      presenter.View.Should().Equal(ScenarioContext.Current.Get<IMainView>()); 
      presenter.Model.Should().Equal(ScenarioContext.Current.Get<IModelContainer>()); 
     } 

... 

     [Then(@"the IMainView stub should extend the IBaseView interface")] 
     public void ThenTheIMainViewStubShouldExtendTheIBaseViewInterface() 
     { 
      var view = ScenarioContext.Current.Get<IMainView>(); 
      view.Should().Be.AssignableFrom<IBaseView>(); 
     } 
    } 
} 

回答

2

事實上,你懷疑你的超時/不使用的目的已經告訴你,已經從這種經驗中學到了很多。 SpecFlow是一種主要用於支持BDD的工具,它是從業務中獲取知識並從成功中定義標準的過程。這絕對不是BDD,但我可以想象,推導這些測試使您能夠仔細考慮您的域。您已經通過規範生成了一些完整的單元測試示例,這很有用。一旦你認爲你的代碼基礎已經足夠成熟並且沒有任何用處,你可能會選擇在將來刪除這些測試(儘管如此),甚至如此,寫下它們就可以澄清事物應該如何工作的願景。

當我第一次開始使用SpecFlow時,我寫了大量的測試,其粒度非常接近這些粒度。我還寫了一些更高的水平,幾乎系統集成水平。現在事後看來,我的意見發生了變化,我發現自己在nUnit和SpecFlow中的更高級別的測試中編寫了類似低級別的測試。每種技術都有它的位置,我發現從Specflow到c#綁定的轉換爲低級別測試增加了一層複雜性。

雖然不要放棄SpecFlow/Gherkin/BDD,但它在單元測試時具有一些很好的優勢,當您在正確的粒度級別使用它時。你只需要找到你滿意的水平。對我而言,這是我能夠拔出一個功能文件並與某人通過咖啡討論的地方。

+0

謝謝你,AlSki。你的話是令人鼓舞的,我會堅持下去。我會問你同樣的問題,我做湯姆;是否有更好的爲WinForms定製的工具可以執行我在文章中強調過的相同的域問題? – Heliac 2013-03-05 08:09:57

+1

湯姆,我一定錯過了你正在使用WinForms :-)你的代碼實際上更多的是關於MVC模式和問題分離而不是UI技術。我打算建議看一下CAB,但是在我的搜索鏈接中,我發現這個http://stackoverflow.com/questions/654722/implementing-mvc-with-windows-forms,它看起來像覆蓋了更多深度比我要適應在這裏。 :-) – AlSki 2013-03-05 12:04:41

1

你使用SpecFlow對我的理解是它的初衷,我不認爲然而,我認爲你仍然「知道了」,你所做的仍然是一個很好的用法。

據我所知,SpecFlow用於描述行爲 - 我不會說這些情景就是這樣的行爲。但是,您已經設法用適當的語言描述您的要求併爲它們編寫測試,所以我認爲這也證明了它在這方面的有用性。祝你好運!

+0

謝謝湯姆。如果您不得不堅持使用WinForms,那麼您會親自建議如何將其作爲一個優秀的項目架構構建工具? – Heliac 2013-03-05 08:06:14