This is a migrated thread and some comments may be shown as answers.

Mock a CompiledQuery with Entity Framework?

3 Answers 213 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
JensH
Top achievements
Rank 1
JensH asked on 30 Sep 2011, 01:03 PM
Hello!
I have troubles with getting my test case to work, possibly due to a lack of understanding of the principles to set this up correctly.

Environment: Entity Framework and .NET 4, Visual Studio 2010, MS SQL Server 2008

What I would like to do, is to test a short method "TryToGetObject", which is defined in BaseClass<T>.
The method takes an object named "importObject" of type "SomeEntityType".

The method passes this object to the compiled query and uses the property "ItemId" of it to find the matching data in the Entity Framework ObjectContext (if it exists).

For this test I wanted to mock the ObjectContext, so that I can avoid a real database access.

Here is my setup (Code is provided below):
  • SomeObjectContext: The Entity Framework context that provides access to the database
  • SomeEntityType: The type that represents the data of a table in the database
  • Class Baseclass<T>: Provides the pasic functionality for all derived classes.
  • DerivedClass<SomeEntityType>: Derives from BaseClass<T> and overrides property "CompiledQueryGetObject" to provide the specific implementation of the compiled query.
  • UnitTest1.TestMethod1() shows various attempty to mock the acces to the query, all of which do not work. The comments above them show the particular error message.

Can anyone please provide me some guidance what I am doing wrong?


Thank you in advance and best wishes,
Jens

BaseClass<T>

namespace MyNamespace
{
  /// <summary>
  /// Provides methods to access a database table.
  /// </summary>
  public abstract class BaseClass<T> where T : EntityObject
  {
    #region Properties
 
    /// <summary>
    /// Is overriden in derived classes.
    /// </summary>
    protected abstract Func<SomeObjectContext, T, T> CompiledQueryGetObject { get; }
 
    /// <summary>
    /// Context for database operations (EntityFramework)
    /// </summary>
    protected SomeObjectContext Context { get; set; }
 
    #endregion Properties
 
    #region Constructors
 
    /// <summary>
    /// Creates a new instance of type BaseClass.
    /// </summary>
    /// <param name="context">The current database context.</param>
    public BaseClass(SomeObjectContext context)
    {
      this.Context = context;
    }
 
    #endregion Constructors
 
    #region Public methods
 
    /// <summary>
    /// Tries to retrieve the given object from the database by its unique database constraints.
    /// </summary>
    /// <param name="importObject">Given entity object.</param>
    /// <returns>Object if found, otherwise null</returns>
    public T TryToGetObject(T importObject)
    {
      T existingObject = this.CompiledQueryGetObject(this.Context, importObject);
      return existingObject;
    }
 
    #endregion Public methods
  }
}

DerivedClass<SomeEntityType>
namespace MyNamespace
{
  /// <summary>
  /// Provides methods to access the SomeEntityType database table.
  /// </summary>
  public class DerivedClass : BaseClass<SomeEntityType>
  {
    #region Compiled queries
 
    /// <summary>
    /// A compiled query to look for an entity by its unique identifier(s) column(s).
    /// </summary>
    private static readonly Func<SomeObjectContext, SomeEntityType, SomeEntityType> m_LocalCompiledQueryGetObject = CompiledQuery.Compile<SomeObjectContext, SomeEntityType, SomeEntityType>(
        (context, importSomeEntityType) => context.SomeEntityTypes.SingleOrDefault(someEntityInstance => someEntityInstance.ItemId == importSomeEntityType.ItemId));
 
    #endregion Compiled queries
 
    #region Constants
 
    /// <summary>
    /// The EntitySetName of the processor that is accessed by this class.
    /// </summary>
    private const string LocalEntitySetName = "SomeEntityType";
 
    #endregion Constants
 
 
    #region Constructor
 
    /// <summary>
    /// Creates a new instance of type DerivedClass.
    /// </summary>
    /// <param name="context">The current database context.</param>
    public DerivedClass(SomeObjectContext context)
      : base(context)
    {
    }
 
    #endregion Constructor
 
    #region Properties
 
    /// <summary>
    /// Compiled query to get an object by the unique database constraint; Is overriden in derived classes.
    /// </summary>
    protected override Func<SomeObjectContext, SomeEntityType, SomeEntityType> CompiledQueryGetObject
    {
      get
      {
        return DerivedClass.m_LocalCompiledQueryGetObject;
      }
    }
 
    #endregion Properties
  }
}

The test class:
namespace TestProject1
{
  [TestClass]
  public class UnitTest1
  {
    [TestMethod]
    public void TestMethod1()
    {
      // Arrange
      // -------
      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, TimeSpan.FromSeconds(300)))
      {
        SomeObjectContext mockedContext = Mock.Create<SomeObjectContext>(Constructor.Mocked, Behavior.CallOriginal);
 
        SomeEntityType emptyQueryObject = new SomeEntityType();
        emptyQueryObject.ItemId = 1;
        emptyQueryObject.CountryId = 1;
        emptyQueryObject.StyleColorSetId = 1;
 
        SomeEntityType returnThisObject = new SomeEntityType();
        returnThisObject.ItemId = 2;
        returnThisObject.CountryId = 2;
        returnThisObject.StyleColorSetId = 2;
 
        DerivedClass classUnderTest = new DerivedClass(mockedContext);
 
        // System.ArgumentException: Direct null valued argument for non-public member is not supported. Use ArgExpr.IsNull<T>() or other members from ArgExpr wrapper class to provide your specification.
        Mock.NonPublic.Arrange<DerivedClass>(classUnderTest, "m_LocalCompiledQueryGetObject", Arg.IsAny<SomeObjectContext>(), Arg.IsAny<SomeEntityType>())
                      .IgnoreArguments()
                      .Returns(CompiledQuery.Compile<SomeObjectContext, SomeEntityType, SomeEntityType>(
                        (paramContext, importSomeEntityType) => mockedContext.SomeEntityTypes.SingleOrDefault(someEntityInstance => someEntityInstance.ItemId == importSomeEntityType.ItemId)));
         
        //Test method TestProject1.UnitTest1.TestMethod1 threw exception:
        //System.ArgumentException: Expression of type 'System.Func`3[MyNamespace.SomeObjectContext,MyNamespace.SomeEntityType,MyNamespace.SomeEntityType]' cannot be used for return type 'MyNamespace.SomeEntityType'
        Mock.NonPublic.Arrange<SomeEntityType>(classUnderTest, "CompiledQueryGetObject")
                      .IgnoreArguments()
                      .Returns(returnThisObject);
 
        // This one is only possible if I set "InternalsVisibleTo" attribute and use the DerivedClass_Accessor instead
        //// Test method TestProject1.UnitTest1.TestMethod1 threw exception:
        //// System.ArgumentException: Direct null valued argument for non-public member is not supported. Use ArgExpr.IsNull<T>() or other members from ArgExpr wrapper class to provide your specification.
        Mock.Arrange(() => classUnderTest.CompiledQueryGetObject(Arg.IsAny<SomeObjectContext>(), Arg.IsAny<SomeEntityType>()))
                                           .IgnoreArguments()
                                           .Returns(returnThisObject);
 
        // Act
        // -------
        SomeEntityType receivedObject = classUnderTest.TryToGetObject(emptyQueryObject);
 
        // Assert
        // ------
        Assert.IsNotNull(receivedObject);
 
        // Some more Asserts...
      }
    }
 
    private SomeEntityType returnEntity()
    {
      SomeEntityType returnObject = new SomeEntityType();
      return returnObject;
    }
  }
}

3 Answers, 1 is accepted

Sort by
0
Ricky
Telerik team
answered on 05 Oct 2011, 11:49 AM
Hi Jensh,
Thanks again for sending the issue. In order to address your problem lets start considering your following code snippet:

  Mock.NonPublic.Arrange<DerivedClass>(classUnderTest, "m_LocalCompiledQueryGetObject"Arg.IsAny<SomeObjectContext>(), Arg.IsAny<SomeEntityType>())
                              .IgnoreArguments()
                              .Returns(CompiledQuery.Compile<SomeObjectContextSomeEntityTypeSomeEntityType>(
                                (paramContext, importSomeEntityType) => mockedContext.SomeEntityTypes.SingleOrDefault(someEntityInstance => someEntityInstance.ItemId == importSomeEntityType.ItemId)));

Here you are trying to mock a non-public field  "m_LocalCompiledQueryGetObject" but you can't do it since it is not possible to intercept a field in .net.

Secondly, if you are using Arg matchers in non-public mocking then it has to be accessed via ArgExpr not Arg class.That is the reason you are getting the "direct null argument" exception.

In case of cast exception :

         //Test method TestProject1.UnitTest1.TestMethod1 threw exception: 
 
                //System.ArgumentException: Expression of type 'System.Func`3[MyNamespace.SomeObjectContext,MyNamespace.SomeEntityType,MyNamespace.SomeEntityType]' cannot be used for return type 'MyNamespace.SomeEntityType'
 
                Mock.NonPublic.Arrange<SomeEntityType>(classUnderTest, "CompiledQueryGetObject").Returns(returnThisObject);


The return type of CompiledQueryGetObject is different from SomeEntityType , thus during reflection its throwing such exception.

Hope this information is useful.
  
Kind Regards,
Mehfuz
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
JensH
Top achievements
Rank 1
answered on 06 Oct 2011, 03:07 PM
Hello Ricky,

Thank you very much for your answer.
It shows clearly that I was misunderstanding some fundamental things here and therefore working on the wrong things.
The above - wrong - examples were added mainly to show you that I at least tried something. I have spend many days on this and of course had a lot of attempts more what than I included in the snippet above.

When I started this, I had expected that the tests should work by mocking the ObjectContext SomeObjectContext and the appropriate EntitySet SomeEntityType:
Mock.Arrange(() => this.m_Context.SomeEntityTypes).ReturnsCollection(this.getFakeCollection());

Where this.getFakeCollection() returns a IEnumerable<SomeEntityType>.

I had expected the Compiled Query would work on the mocked EntitySet.

I seems like this was wrong, too, all seems to indicate that the Compiled Query works directly on the database and so my previous attempts were doomed right from the beginning.

Yet, I do not know how to get to a solution. So my question remains:
Do you have any advice about what else do I need to arrange instead to make my test(s) independent from the real database?


0
Accepted
Ricky
Telerik team
answered on 11 Oct 2011, 12:33 PM
Hi Jensh,

Thanks again for getting back to us. In order to mock entity framework data container i would recommend you to take a look at this following post from online documentation:

http://www.telerik.com/help/justmock/basic-usage-entity-framework-mocking.html


There is nothing wrong with your example. However, the problem is that you can't mock or intercept a field. If you still want to mock the m_context you can wrap it around a property then mock it with ReturnsColelction. Or if you can pass the m_context via a constructor then you can first mock the SomeEntityType and then pass it over when creating the instance of the container.

Hope that answers your question.


Kind Regards,
Mehfuz
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Tags
General Discussions
Asked by
JensH
Top achievements
Rank 1
Answers by
Ricky
Telerik team
JensH
Top achievements
Rank 1
Share this question
or