jeudi 4 août 2016

Moq.Setup Expression of type 'System.Web.Mvc.ActionResult' cannot be used for return type 'System.Web.Mvc.ActionResult'

I'm working on some legacy code (def: untested code - some well designed some not) and trying to develop some tests to confirm recent changes did what they expected etc. I'm running into an issue where I'm trying to force a method that has a try{catch} block in it to throw an exception using Moq. When I try to run the test it fails during the mock.Setup call with System.ArgumentException "Expression of type 'System.Web.Mvc.ActionResult' cannot be used for return type 'System.Web.Mvc.ActionResult'".

The basic setup of the code:

Interface for FilterController...

public interface IFilterController
{
    ActionResult DeleteFilter(string reportFilter, bool customReport = true);
}

FilterController class...

public class FilterController : BaseController, IFilterController
{
    public FilterController(
        IServiceFactory serviceFactory,
        IAwsServiceFactory awsServiceFactory,
        IReportServiceFactory reportServiceFactory,
        IAzureServiceFactory azureServiceFactory)
        : base(typeof(FilterController), serviceFactory, awsServiceFactory, reportServiceFactory, azureServiceFactory)
    {
    }

     // method under test
    public ActionResult (string reportFilter, bool customReport = true) {
        try {
             // NOTE: I have trimmed down the actual code in the try block significantly for brevity - I should be able to hook onto something here as a way to mock something throwing an exception
             var customReportFilterService = _serviceFactory.CreateCustomReportFilterService();
            var emailReportSettingService = _serviceFactory.CreateEmailReportSettingService();
            string message = string.Empty;
            JsonReturnType type = JsonReturnType.DisplayMessage; // an enum

            var filter = customReportFilterService.GetReportFilterByHash(SessionHelper.User.CustomerId, reportFilter, initLinkedProjects: true);
            return JsonActionResult(type, ajaxMessage: message, redirectTo: filter == null ? null : string.Format("Report/{0}", filter.ReportName));
        }
        catch (Exception ex)
        {
            return JsonActionResult(JsonReturnType.Error, ajaxMessage: "There was an error in deleting the filter.");
        }
    }         
}

BaseController class...

public class BaseController : Controller
{
    private readonly ProgressController _progressController;
    protected IServiceFactory _serviceFactory;
    protected IAwsServiceFactory _awsServiceFactory;
    protected IReportServiceFactory _reportServiceFactory;
    protected IAzureServiceFactory _azureServiceFactory;
    protected IApplicationSettingService _applicationSettingService;
    protected IReportMonitorService _reportMonitorService;
    protected ISymmetricAlgorithmProvider HiddenEncrypter { get; set; }
    private Stopwatch _watch;
    private bool _timePageEnabled;
    private bool _maintenance;
    private int _pageLoadThreshold;
    private readonly ILog Logger;

    public BaseController(Type type, IServiceFactory serviceFactory, IAwsServiceFactory awsServiceFactory, IReportServiceFactory reportServiceFactory, IAzureServiceFactory azureServiceFactory)
    {
        Logger = LogManager.GetLogger(type);
        _progressController = new ProgressController();
        _serviceFactory = serviceFactory;
        _awsServiceFactory = awsServiceFactory;
        _reportServiceFactory = reportServiceFactory;
        _azureServiceFactory = azureServiceFactory;
        _applicationSettingService = _serviceFactory.CreateApplicationSettingService();
        _reportMonitorService = _serviceFactory.CreateReportMonitorService();

        _watch = new Stopwatch();
        _timePageEnabled = _applicationSettingService.ReadApplicationSettingFromCache<bool>(CC.Data.Model.Constants.ApplicationSettings.CheckSlowPageLoad, true);
        _pageLoadThreshold = _applicationSettingService.ReadApplicationSettingFromCache<int>(CC.Data.Model.Constants.ApplicationSettings.PageLoadThreshold, 120);
        _maintenance = _applicationSettingService.MaintenanceMode();
    }

   // System.Web.Mvc.ActionResult type mentioned in error
   public ActionResult JsonActionResult(JsonReturnType returnType, string view = null, string ajaxMessage = null, string redirectTo = null, string target = null, object data = null, string popupTitle = null)
    {
        if (returnType == JsonReturnType.LoadContent)
           _progressController.CompleteProgressCache();

        var serializer = new JavaScriptSerializer();
        serializer.MaxJsonLength = Int32.MaxValue;

        var resultData = new { 
            ReturnType = returnType, 
            HtmlView = view, 
            Message = ajaxMessage, 
            RedirectTo = redirectTo, 
            Target = target, 
            CustomData = data, 
            ProjectId = SessionHelper.ProjectId, 
            PopupTitle = popupTitle,
            MaintenanceMode = _maintenance
        };

        ContentResult result;

        result = new ContentResult
        {
                Content = serializer.Serialize(resultData),
                ContentType = "application/json"
        };

        return result;
    }
}

Controller class...

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {
    // stuff
}

Unit Test class...

[TestClass]
public class FilterControllerTest
{
    private FilterController filterController;
    private Mock<IFilterController> filterControllerMock;

    private Mock<IServiceFactory> serviceFactoryMock;
    private Mock<IAwsServiceFactory> awsServiceFactoryMock;
    private Mock<IReportServiceFactory> reportServiceFactoryMock;
    private Mock<IAzureServiceFactory> azureServiceFactoryMock;
    private Mock<IApplicationSettingService> applicationSettingServiceMock;

    [ClassInitialize]
    public static void ClassInit(TestContext context)
    {
    }

    [TestInitialize]
    public void Initialize()
    {
        filterControllerMock = new Mock<IFilterController>();
        serviceFactoryMock = new Mock<IServiceFactory>();
        awsServiceFactoryMock = new Mock<IAwsServiceFactory>();
        reportServiceFactoryMock = new Mock<IReportServiceFactory>();
        azureServiceFactoryMock = new Mock<IAzureServiceFactory>();
        applicationSettingServiceMock = new Mock<IApplicationSettingService>();

        serviceFactoryMock
            .Setup(s => s.CreateApplicationSettingService())
            .Returns(applicationSettingServiceMock.Object);

        filterController = new FilterController(
            serviceFactoryMock.Object
            , awsServiceFactoryMock.Object
            , reportServiceFactoryMock.Object
            , azureServiceFactoryMock.Object);
    }

    [TestCleanup]
    public void Cleanup()
    {
    }

    [ExpectedException(typeof(Exception))]
    [TestMethod]
    public void DeleteFilter_ExceptionThrown_IsCaughtAndLoggedAndReturnsActionResultOfError()
    {

        // Arrange
        filterControllerMock
            .Setup(x => x.DeleteFilter(It.IsAny<string>(), It.IsAny<bool>()))
            .Throws(new Exception());

        // Act
        var result = filterController.DeleteFilter("myfilt", false);
    }
}

In the end all I want to do is have it so when DeleteFilter is called for this test, an error is thrown and then I can assert what is returned from the catch block.

EDIT: have majorly updated the post by suggestion to make it easier to understand where the issue is.

Aucun commentaire:

Enregistrer un commentaire