In questo ultimo periodo ho usato molto il pattern ModelViewPresenter. Per scrivere le mie triadi MVP ho utilizzato la tecnica del presenter first con ottimi risultati. All'interno dei miei unit-test ho fatto ampio uso di Mock Objects ed ho notato una certa ripetitività nel codice prodotto, soprattutto nei metodi di Setup e di TearDown. Con una soluzione simili a quella usata per il refactoring del presenter, ho usato i generics ed una classe base per evitarmi inutili ripetizioni.
Vediamo un esempio concreto. Di seguito un presenter con la sua View ed il suo Model
public interface IMyView
{
void ShowLoggedUser(string s);
}
public interface IMyModel
{
string GetUserName();
}
public class MyPresenter :Presenter<IMyView,IMyModel>
{
public MyPresenter(IMyView view,IMyModel model)
:
base(view, model)
{
}
public void Init()
{
View.ShowLoggedUser("utente:"+ Model.GetUserName());
}
}/p>
Per testare il comportamento del metodo Init devo scrivere una classe di test come la seguente:
public class MyPresenterFixture
{
protected MockRepository _repository;
protected IMyView _view;
protected IMyModel _model;
[SetUp]
public virtual void SetUp()
{
_repository =new MockRepository();
_view = _repository.CreateMock<IMyView>();
_model = _repository.CreateMock<IMyModel>();
}
[TearDown]
public virtual void TearDown()
{
_repository.ReplayAll();
_repository.VerifyAll();
}
[Test]
public void Init_ReadUserNameFormModel_DisplayUserNameView()
{
string user ="pippo";
Expect.Call(_model.GetUserName()).Return(user);
_view.ShowLoggedUser("utente:" + user);
_repository.ReplayAll();
MyPresenter presenter =new MyPresenter(_view, _model);
presenter.Init();
}
}
Se lanciamo il test il risultato é:
1 passed, 0 failed, 0 skipped, took 3,52 seconds.
Ora nel caso volessi scrivere una nuova triade MVP avrei 3 nuovi tipi da testare (es. MyPresenter2, IMyView2, IMyModel2) ed una nuova classe di test che molto probabilmente si chiamerà MyPresenter2Fixture. Ora in questa classe MyPresenter2Fixture il metodo marcato con l'attributo TearDown sarà identico a quello del fratello MyPresenterFixture, mentre il metodo marcato con l'attributo Setup sarà diverso solo per i tipi di View e Model che dovrà moccare. Usando erditarietà e generics si può evitare parecchio copia ed incolla.
public abstract class PresenterFixture<V, M>
{
protected MockRepository _repository;protected V _view;
protected M _model;
[SetUp]
public virtual void SetUp()
{
_repository =new MockRepository();
_view = _repository.CreateMock<V>();
_model = _repository.CreateMock<M>();
}
[TearDown]
public virtual void TearDown()
{
_repository.ReplayAll();
_repository.VerifyAll();
}
}
così facendo possiamo fare refactoring sulla classe MyPresenterFixture in questo modo
public class MyPresenterFixture :PresenterFixture<IMyView,IMyModel>
{
[Test]
public void Init_ReadUserNameFormModel_DisplayUserNameView()
{
string user ="pippo";
Expect.Call(_model.GetUserName()).Return(user);
_view.ShowLoggedUser("utente:" + user);
_repository.ReplayAll();
MyPresenter presenter =new MyPresenter(_view, _model);
presenter.Init();
}
}
ed quindi dimenticarci dei metodi di Setup e TearDown per tutte le classi derivate da PresenterFixture<V,M>.
Qualora fosse necessario è possibile fare l'override dei 2 metodi perchè nella classe base sono dichiarati come metodi virtual.