ChartResult: Testable ASP.NET MVC 3 controller actions that return Chart

Lately I blogged about charts support in ASP.NET MVC 3 beta. This posting showed how to get chart initialized and sent to client quickly. In this posting I will show you how to use Chart with ChartResult to make our chart operations testable.

Model

Let’s start with simple model that returns data for chart. In real life you will use database or other data source in model but I save your time and use simple model.


public class ChartModel
{
   
public virtual IList
GetChartData()
    {
       
return new ArrayList
                   {
                  
new
{ X = DateTime.Now.AddMonths(-2), Y = 200 },
                  
new
{ X = DateTime.Now.AddMonths(-1), Y = 300 },
                  
new { X = DateTime.Now, Y = 500 }
                   };
    }
}

Although this model is taken from my charts posting I have minor change here: GetChartData() method is virtual because otherwise Moq cannot mock it.

Controller

Here is the full code of controller. To make controller testable I created constructor that takes model as argument. Don’t forget that in the real-life scenario this model will communicate with some data store.


public class HomeController : Controller
{
   
private readonly ChartModel
_model;

   
public HomeController(ChartModel
model)
    {
        _model = model;
    }

   
public ActionResult
Index()
    {
        ViewModel.Message =
"Welcome to ASP.NET MVC!"
;

       
return
View();
    }

   
public ChartResult
GetChart()
    {
       
var
data = _model.GetChartData();

       
var chart = new Chart(400, 200, ChartTheme
.Blue)
                    .AddTitle(
"Price enquiries"
)
                    .DataBindTable(data,
"X"
);

       
return new ChartResult(chart, "png"
);
    }

   
public ActionResult
About()
    {
       
return View();
    }
}

In my example application I use custom controller factory. If you want to see it, feel free to download the source code.

ChartResult

Here is my ChartResult class that does nothing more than outputs the chart.


public class ChartResult : ActionResult
{
   
private readonly Chart
_chart;
   
private readonly string
_format;

   
public ChartResult(Chart chart, string
format)
    {
       
if (chart == null
)
           
throw new ArgumentNullException("chart"
);

        _chart = chart;
        _format = format;

       
if (string
.IsNullOrEmpty(_format))
            _format =
"png"
;
    }

   
public Chart
Chart
    {
       
get { return
_chart; }
    }

   
public string
Format
    {
       
get { return
_format; }
    }

   
public override void ExecuteResult(ControllerContext context)
    {
        _chart.Write(_format);
    }
}

I made also chart and format available as properties so you have better control over this action result in your tests.

Test

To test if GetChart() works like expected we will use the following primitive test. I am using nUnit and moq for tests. You can add them both to your test project using NuPack.


[TestFixture]
public class HomeControllerTests
{
    [
Test
]
   
public void
GetChartReturnsChart()
    {
       
var
data = GetChartData();
       
var mock = new Mock<ChartModel
>();
        mock.Setup(c => c.GetChartData())
            .Returns(data);

       
var controller = new HomeController
(mock.Object);
       
var
result = controller.GetChart();

        mock.Verify();
       
Assert.IsNotNull(result, "Result is null"
);
       
Assert.IsInstanceOf<ChartResult>(result, "Result is not ChartResult"
);
    }

   
private static IList
GetChartData()
    {
       
return new ArrayList
                    {
                   
new
{ X = DateTime.Now.AddMonths(-2), Y = 200 },
                   
new
{ X = DateTime.Now.AddMonths(-1), Y = 300 },
                   
new { X = DateTime.Now, Y = 500 }
                    };
    }
}

Running the test gives us the following nice result.

ChartResult tests

So, seems like our controller works like expected.

Conclusion

Although new helper for charts is very powerful and works like charm we still need to write some code to keep our controller actions testable. ASP.NET MVC with its flexible architecture helps us out. All we had to do was to remove model instantiation out from controller and add ChartResult class to our project so we follow they way how ASP.NET MVC controllers work.


Leave a Reply

Your email address will not be published. Required fields are marked *