I'm trying to test a Presenter created using ASP.NET WebFormsMVP. I'm building it using a "Supervising Controller" pattern so the View is responsible for updating itself from the Model. I've simplified down the following example of a page with a textbox, a button & label. You type in the textbox & press the button and the text HelloWorld! <YOUR TEXT>
gets put on the label.
Sample code below, but in a nutshell:
- the button_click will raise a View Event.
- the Presenter subscribes to this ViewEvent and catches the args (i.e. the message)
- the Presenter does the work (string concatenation) and updates the Model
- the view rebinds to the Model.Message property & it all works fine.
//Model
public class HelloWorldModel {
public string Message { get; set; }
}
//Args
public class HelloWorldEventArgs : EventArgs {
public string Message { get; set; }
}
//View
public interface IHelloWorldView : IView<HelloWorldModel> {
event EventHandler<HelloWorldEventArgs> SendMessage;
}
//Presenter
public class HelloWorldPresenter : Presenter<IHelloWorldView>
{
private readonly EventHandler<HelloWorldEventArgs> SendMessageDelegate;
public HelloWorldPresenter(IHelloWorldView view) : base(view)
{
SendMessageDelegate = ((s, e) => SendMessageReceived(e.Message));
View.SendMessage += SendMessageDelegate;
}
public override void ReleaseView()
{
View.SendMessage -= SendMessageDelegate;
}
public void SendMessageReceived(string message)
{
View.Model.Message = string.Format("Hello World! - {0}", message);
}
}
//View implementation
[PresenterBinding(typeof(HelloWorldPresenter))]
public partial class HelloWorld : MvpPage<HelloWorldModel>,IHelloWorldView
{
protected void EchoButtonClick(object sender, EventArgs e)
{
if(SendMessage != null)
{
var args = new HelloWorldEventArgs {Message = MessageTextBox.Text};
SendMessage(sender, args);
}
}
public event EventHandler<HelloWorldEventArgs> SendMessage;
}
My problem lies with the testing though.
Since the View is responsible for updating itself from the model, the Presenter only sets the Model.Message Property... so in a Unit Test, I want to do the following.
- Mock my
IHelloWorldView
- Instantiate my Present with the Mock.
- Trigger the event on the Mock
- Verify that the present set the Model.Message Property of the Mock.
[TestMethod]
public void TestMethod1()
{
var input = "My Message";
var expected = string.Format("Hello World! - {0}", input);
var mock = new Mock<IHelloWorldView>
{
DefaultValue = DefaultValue.Mock
};
var pres = new HelloWorldPresenter(mock.Object);
mock.Raise(m =>
m.SendMessage += null,
new HelloWorldEventArgs { Message = input });
mock.VerifySet(view =>
view.Model.Message = It.Is<string>(s => s == expected),
Times.Once());
}
But this won't work unless I explicity mark the Message
Property of my Model as virtual which I don't really want to do. e.g.
//Model
public class HelloWorldModel {
public string Message { get; set; }
}
My Other Option is to use a Passive View Pattern and expose the asp:label Text as a string property on the IHelloWorldView and set that directly from the Presenter... and then I should be able to test it.
- Is Passive View a better approach from a testing point of view ?
- Is it necessary that I should have to either Also Mock my Model (which I'm not sure is possible in WebFormsMVP? OR
- That I need to make all the properties of my Model Virtual OR
- Have I missed the point somewhere?
You are setting up the view-mock with DefaultValue.Mock, so your view will have an initialized Model property. Instead of utilizing Moq to check the value of the model, just check the Message directly on your model:
The beauty of this small (but semantically equal) change is that your mock suddenly becomes a stub and your are testing end result instead of nitty gritty implementation details. As for your questions, its a good thing to design your system in terms of testability, but I would not start to virtualize or interface everything just to make it Moq-friendly; make a SOLID architecture, not necessary Moq-able.