Bug?: Unit Testing Csla.Web.Mvc.Controller results in access exception

Bug?: Unit Testing Csla.Web.Mvc.Controller results in access exception

Old forum URL: forums.lhotka.net/forums/t/10471.aspx


lacota posted on Tuesday, June 28, 2011

This is a strange bug. I have a controller derived from Csla.Web.Mvc.Controller. When I attempt to unit test it, checking to see if it retrieves the correct view, I get the following error when calling, "return View()";

Attempt by method 'MvcPoc.UI.WebApplication.Controllers.MyController.List()' to access method 'System.Web.Mvc.Controller.View()' failed.

On closer inspection this line does not seem to work;

ViewData.Model = MyItemList.Get();

Model remains null (and yes 'Get' is returning a list not null). The strange thing is, the model is null in the debugger but if I add this line,

var isnull = (ViewData.Model == null);

isnull remains false!? There must be some quantum computing going on as the Model seems to be both null and not null at the same time.

If I change the base class to the System.Web.Mvc.Controller this problem goes away and the test passes.

 

The other strange thing is that if I step through the code, including the csla code, I don't see any problems with the csla controller. In fact I don't even step into the csla controller.

This led me to believe that the problem may have to do with reflection in the system controller class. So I derived my own controller from it and tried that. No problem.

I then built my controllerbase to look the same as the one in csla. Again, no problem

I then derived my base class from csla controller and got the access error again.

So then I thought this was a scope thing, so I moved my controllerBase to an external assembly, derived from system controller and still no problem. The problem returned when I derived it from the csla controller.

The only way to replicate this error is to use the csla controller. I'm at an impasse here. This problem is easy to test;

BillG replied on Monday, July 18, 2011

I am running into the same issue

I have done the same tests and came to the same set of results

Has anyone else run into this?

I've looked through the Latest EBook and accompaning code hoping to find a sample or help in testing and was surprised to see no tests for the MVC Project

Hopefully someone else has come across this and can point to a solution to this problem

 

 

JonnyBee replied on Monday, July 18, 2011

Can you post a sample project with unit test project that fails?

Does your MVC app and unit test projects also include reference to Csla.Web?

I created the same project on my machine and the UnitTests run just fine.

Csla.Web.Mvc.Conttroller inherits from System.Web.Mvc.Controller and only adds 2 new methods:

 

 

BillG replied on Monday, July 18, 2011

Hi

Thanks for the quick response

To See the Same Results

Create a Sample Razor Application and include the Unit Tests

Compile and Run the default Controller Tests (About and Home)

All works

Change to inherit from CSLA Controller

Run the Test -

Both Tests Fail

Any Help would be greatly appreciated

 

BillG replied on Monday, July 18, 2011

HI

Yes I do have the References to CSLA, CSLA.WEB and CSLA.MVC

JonnyBee replied on Monday, July 18, 2011

I did exactly that - and changed both HomeController and AboutController to inherit from Csla.Web.Mvc.Controller.

And all unit tests run just fine. I am using the latest CSLA version from svn trunk.

 

 

RockfordLhotka replied on Monday, July 18, 2011

Odds are that the unit test project isn't configured correctly in some way.

If the failure is on your .Get() factory call, then you must remember to configure your unit test project so the data portal works, and so your data access code works.

I usually configure the data portal to be local and to use a mock DAL. Sometimes I'll use a remote data portal with a mock DAL.

But it is a pretty good bet that the problem isn't in the controller, but rather is deeper - like when the data portal tries to talk to the app server.

OR, the second most likely issue tends to revolve around the use of a custom principal. Unit test frameworks sometimes make this difficult, because the same thread moves between different AppDomains (the test runner, and the test itself). Also, tests that rely on authz rules need some pre-existing authn condition to be set in the test, or better yet in a test initializer.

BillG replied on Monday, July 18, 2011

ConfusedThanks for the Response

I have other tests that are exercising the DAL and In those cases there is a separate Mock DAL and the tests work correctly

But It does seem to be an access issue as the Exact Error is
Attempt by method 'Ecord.Razor.Controllers.HomeController.Index()' to access method 'System.Web.Mvc.Controller.View()' failed.

and the troubleshooting tip is

A MethodAccessException exception is thrown when there is an invalid attempt to access a private or protected method inside a class.

Associated Tips

If the access level of a method in a class library has changed, recompile any assemblies that reference the library.

In this case there is no Data Access or Security Checking

The Controllers simply serve up the default views so No DAL and No real use of CSLA

So StrangeConfused

 

BillG replied on Tuesday, July 19, 2011

Hi

Some Further Information

I implemented a MVCFakes in hopes of isolating the root cause

I was able to get the Following error when creating a Fake Controller Context

Unable to cast object of type 'Ecord.Razor.Controllers.HomeController' to type 'System.Web.Mvc.ControllerBase'

Again, this error only happens when I am inheriting CSLA.Web.MVC.Controller

 

JonnyBee replied on Tuesday, July 19, 2011

Hi,

Have you checked to verify that Csla.Web.Mvc and your project references the same version of System.Web.Mvc?

BillG replied on Tuesday, July 19, 2011

Hi

Thanks again for the Quick reply

In order to confirm I have recompiled the CSLA source and updated the references

They are the Same

Thanks angain for any help

 

G.R. replied on Thursday, August 25, 2011

Hi, 

I had the same problem in my project. I simply reimplemented the Csla.Web.Mvc.Controller class in my project and everything is working fine. I'm not sure of the exact cause but it's a nice workaround...  

Guillaume,

BillG replied on Thursday, September 01, 2011

Hi Guillaume

Thanks for your response

Please explain what you meant by implementing it in your own project.  Does this mean you copied the files into your MVC program and did not use the CSLA DLL?

I look forward to your response

 

Have a GREAT Day

G.R. replied on Monday, September 05, 2011

Hi Bill,

Here's the code I added into my project :

 

using System;

using Csla;

using Csla.Core;

using Csla.Server;

using Csla.Web.Mvc;

 

namespace MyProjectNamespace

{

    /// <summary>

    /// Overrides the CSLA controller to handle error.

    /// </summary>

    public class MyCSLAController : System.Web.Mvc.Controller

    {

        /// <summary>

        /// Performs a Save() operation on an

        /// editable business object, with appropriate

        /// validation and exception handling.

        /// </summary>

        /// <typeparam name="T">Type of business object.</typeparam>

        /// <param name="item">The business object to insert.</param>

        /// <param name="forceUpdate">true to force Save() to be an update.</param>

        /// <returns>true the Save() succeeds, false if not.</returns>

        protected internal virtual bool SaveObject<T>(T item, bool forceUpdate)

            where T : class, ISavable

        {

            return this.SaveObject<T>(

                item, 

                delegate(T i) { this.UpdateModel<T>(i); }, 

                forceUpdate);

        }

 

        /// <summary>

        /// Performs a Save() operation on an

        /// editable business object, with appropriate

        /// validation and exception handling.

        /// </summary>

        /// <typeparam name="T">Type of business object.</typeparam>

        /// <param name="item">The business object to insert.</param>

        /// <param name="updateModel">Delegate that invokes the UpdateModel() method.</param>

        /// <param name="forceUpdate">true to force Save() to be an update.</param>

        /// <returns>true the Save() succeeds, false if not.</returns>

        protected bool SaveObject<T>(T item, Action<T> updateModel, bool forceUpdate)

            where T : class, ISavable

        {

            bool result;

 

            try

            {

                this.ViewData.Model = item;

 

                if (updateModel != null)

                {

                    updateModel(item);

                }

 

                this.ViewData.Model = item.Save(forceUpdate);

                result = true;

            }

            catch (Csla.DataPortalException ex)

            {

                if (ex.BusinessException != null)

                {

                    this.ModelState.AddModelError(string.Empty, ex.BusinessException.Message);

                }

                else

                {

                    this.ModelState.AddModelError(string.Empty, ex.Message);

                }

 

                result = false;

            }

            catch (Exception ex)

            {

                this.ModelState.AddModelError(string.Empty, ex.Message);

                result = false;

            }

 

            return result;

        }

 

        /// <summary>

        /// Loads a property's managed field with the 

        /// supplied value calling PropertyHasChanged 

        /// if the value does change.

        /// </summary>

        /// <typeparam name="T">

        /// Type of the property.

        /// </typeparam>

        /// <param name="target">

        /// Object on which to call the method. 

        /// </param>

        /// <param name="propertyInfo">

        /// PropertyInfo object containing property metadata.</param>

        /// <param name="newValue">

        /// The new value for the property.</param>

        /// <remarks>

        /// No authorization checks occur when this method is called,

        /// and no PropertyChanging or PropertyChanged events are raised.

        /// Loading values does not cause validation rules to be

        /// invoked.

        /// </remarks>

        protected void LoadProperty<T>(object target, PropertyInfo<T> propertyInfo, T newValue)

        {

            new ObjectManager().LoadProperty(target, propertyInfo, newValue);

        }

 

        /// <summary>

        /// Manage the CSLA objects

        /// </summary>

        private class ObjectManager : Csla.Server.ObjectFactory

        {

            /// <summary>

            /// Load the property

            /// </summary>

            /// <typeparam name="P">the generic type</typeparam>

            /// <param name="obj">the object</param>

            /// <param name="propertyInfo">the CSLA property</param>

            /// <param name="newValue">the new value</param>

            public new void LoadProperty<P>(object obj, PropertyInfo<P> propertyInfo, P newValue)

            {

                this.LoadProperty(obj, propertyInfo, newValue);

            }

        }

    }

}

Copyright (c) Marimer LLC