AntonioGanci

Il blog di Antonio Ganci
posts - 201, comments - 420, trackbacks - 31

Un esempio concreto di sviluppo di una GUI in TDD (Seconda parte)

Procediamo con la seconda parte (la prima la trovate qui). Tra i link mi sono dimenticato di citare questo post di Luka.

Il secondo test che scriveremo sarà molto simile al primo e cioè riempiremo la combobox con l'elenco dei chart pane già presenti, evidenziato in rosso nella figura seguente:

Select Symbol screen shot 2

Il model segnala la modifica della lista dei pane tramite l'evento ChartPaneListChanged a cui il presenter è sottoscritto e richiamerà la view chiamando il metodo per riempire la combobox. Iniziamo controllando che il presenter si sottoscriva all'evento, aggiungendo al metodo SetUp della classe SelectSymbolPresenterTests il seguente codice:

            _model.ChartPaneListChanged += null;

            _chartPaneListChangedRaiser = LastCall.Repeat.Once().IgnoreArguments().GetEventRaiser();

Dopodichè il codice del test sarà:

        [Test]

        public void WhenTheModelFiresTheChartPaneListEventThePresenterCallsTheViewToFillTheList()

        {

            string[] chartPaneNames = new string[] {"One", "Two", "Three", "Four", "Five"};

            _view.FillChartPaneList(chartPaneNames);

            LastCall.Repeat.Once();

            _mocks.ReplayAll();

 

            new SelectSymbolPresenter(_model, _view);

            _chartPaneListChangedRaiser.Raise(_model, new StringListEventArgs(chartPaneNames));

        }

La variabile chartPaneNames contiene l'elenco dei nomi dei pane che ci arrivano dall'evento ChartPaneListChanged, nelle due righe successive ci aspettiamo che il presenter chiami il metodo della view FillChartPaneList; dopodichè creiamo il presenter ed infine simuliamo l'evento.

Vediamo ora un caso in cui l'interazione parte dalla view. Se l'utente seleziona un elemento dalla combobox dei chart pane deve essere selezionato il radio button "Plot in the chart pane". Partiamo dal sequence diagram:

SelectedChartPaneChanged Sequence Diagram

In questo caso il model non è coinvolto. Vediamo come scrivere il test: per prima cosa dobbiamo controllare che il presenter si sottoscriva all'evento SelectedChartPaneChanged, il codice da aggiungere al metodo SetUp sarà:

            _view.SelectedChartPaneChanged += null;

            _selectedChartPaneChangedRaiser = LastCall.Repeat.Once().IgnoreArguments().GetEventRaiser();

Per fare compilare il codice dobbiamo aggiungere l'evento alla view la quale con le modifiche precedenti diventerà:

using System;

 

namespace PresenterFirstExample

{

    public interface ISelectSymbolView

    {

        event EventHandler SelectedChartPaneChanged;

        void FillSymbolNameList(string[] symbolNames);

        void FillChartPaneList(string[] chartPaneNames);

        void SelectPlotInPaneOption();

    }

}

Vediamo ora il nostro test:

        [Test]

        public void WhenTheViewFiresTheSelectedChartPaneChangedEventThePresenterCallTheViewToSelectThePlotInPaneOption()

        {

            _view.SelectPlotInPaneOption();

            LastCall.Repeat.Once();

            _mocks.ReplayAll();

 

            new SelectSymbolPresenter(_model, _view);

            _selectedChartPaneChangedRaiser.Raise(_view, EventArgs.Empty);

        }

Se proviamo a lanciarlo chiaramente fallisce con il seguente messaggio:

TestCase 'SelectSymbolPresenterTests.SetUp.WhenTheViewFiresTheSelectedChartPaneChangedEventThePresenterCallTheViewToSelectThePlotInPaneOption.TearDown' failed: ISelectSymbolView.add_SelectedChartPaneChanged(any); Expected #1, Actual #0.

Modifichiamo il presenter per fare passare il test:

using System;

 

namespace PresenterFirstExample

{

    public class SelectSymbolPresenter

    {

        private readonly ISelectSymbolModel _model;

        private readonly ISelectSymbolView _view;

 

        public SelectSymbolPresenter(ISelectSymbolModel model, ISelectSymbolView view)

        {

            _model = model;

            _view = view;

            _model.SymbolNameListChanged += new EventHandler<StringListEventArgs>(Model_SymbolNameListChanged);

            _model.ChartPaneListChanged += new EventHandler<StringListEventArgs>(Model_ChartPaneListChanged);

            _view.SelectedChartPaneChanged += new EventHandler(View_SelectedChartPaneChanged);

        }

 

        void Model_SymbolNameListChanged(object sender, StringListEventArgs e)

        {

            _view.FillSymbolNameList(e.StringList);

        }

 

        void Model_ChartPaneListChanged(object sender, StringListEventArgs e)

        {

            _view.FillChartPaneList(e.StringList);

        }

 

        void View_SelectedChartPaneChanged(object sender, EventArgs e)

        {

            _view.SelectPlotInPaneOption();

        }

    }

}

Come si può vedere questo modo di procedere è incrementale in quanto aggiungiamo man mano le cose che servono garantendo che non cambiamo i comportamenti già testati. Non abbiamo ancora scritto nè il model nè la view. Proviamo a scrivere il codice della view; il compito è banale, si tratta di implementare in una form (ma potrebbe essere una pagina web o altro) l'interfaccia ISelectSymbolView:

using System;

using System.Windows.Forms;

 

namespace PresenterFirstExample

{

    public partial class SelectSymbolForm : Form, ISelectSymbolView

    {

        public SelectSymbolForm()

        {

            InitializeComponent();

        }

 

        public event EventHandler SelectedChartPaneChanged;

 

        public void FillSymbolNameList(string[] symbolNames)

        {

            _symbolNamesComboBox.Items.Add(symbolNames);

        }

 

        public void FillChartPaneList(string[] chartPaneNames)

        {

            _paneNamesComboBox.Items.Add(chartPaneNames);

        }

 

        public void SelectPlotInPaneOption()

        {

            _plotOnPaneRadioButton.Checked = true;

        }

 

        private void PaneNamesComboBox_SelectedIndexChanged(object sender, System.EventArgs e)

        {

            if (SelectedChartPaneChanged != null)

            {

                SelectedChartPaneChanged(this, EventArgs.Empty);

            }

        }

    }

}

Il codice dentro alla form non è testato, quindi se ad esempio il metodo FillSymbolNamesList contenesse un bug questo non verrebbe rilevato dai test, c'è da dire che questo codice è il minimo indispensabile, infatti come vedete la view è stateless cioè non contiene field inoltre non dipende ne dal model ne dal presenter e quindi il codice che butterei via se sostituissi la view è minimo.

Prima di vedere il tutto in azione ci manca solo più l'implementazione del model.

Continua... 

Print | posted on sabato 3 marzo 2007 13:33 | Filed Under [ Extreme Programming ]

Feedback

Gravatar

# Un esempio concreto di sviluppo di una GUI in TDD (Terza ed ultima parte)

Ecco la terza ed ultima parte di un caso reale di implementazione di MVP in .NET tramite il TDD
04/03/2007 17:35 | AntonioGanci
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET