Using the business objects to invoke a mock DAL ... couple questions ...

Using the business objects to invoke a mock DAL ... couple questions ...

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


mattruma posted on Thursday, February 03, 2011

We really want to be able to test our business objects without hitting a database ... so I spent some time with the CSLA book and watched the 3.8 Core videos and so this concept of using the business object to invoke the DAL. I saw in the demo where Rocky is loading from an ADO DAL and a MOCK DAL. This looks like just what we need!

A couple of changes we are going to make is actually have something like ICustomerFactory and ICustomer, so we can pass a Customer business object that implements ICustomer to the ICustomerFactory ... this way we don't need to pass in a ton of parameters.  I'm curious if this makes sense and if other have a better way.

The other thing, is that with the Mock DAL we would like to "fill" it with different data at different times ... mainly for different test scenarios. I noticed in Rocky's Mock DAL he is prepopulating it with data. I would like to do this dynamically ... or even something like an in memory database? Again, I just need the data to be tweaked for each different test scenario. Is anyone else trying to do this ... and if so, I'd appreciate any help you could provide!

Thanks! Smile

JonnyBee replied on Thursday, February 03, 2011

Hi,

ObjectFactoryAttribute is updated for Csla 4.1 to accept an Interface and can easily be downported to Csla 3.8.

Usage like this:
[ObjectFactory(typeof(ICustomerFactory))]

In http://cslacontrib.codeplex.com you will also find samples on using the new ObjectFactoryAttribute and a custom ObjectfactoryLoader that uses MEF to load data into the BO. This factory loader uses a static IoC class (wraps a MEF container) that can be configured separately for your tests -  ie use separate Factory classes for the same BO in different test cases.

You should also be aware that the ObjectFactory is responsible for both creating the objects and set the correct state, like MarkOld and MarkNew.

I would be very cautious about creating ICustomer interfaces - and rather create an ICustomerFactory and CustomerFactory classes (implementing ICustomerFactory)  that loads the data. In the factory classes you'll use methods from the ObjectFactory base class to set the internal values of you BOs with either:

using (BypassPropertyChecks(customer))
{
      customer.Name = someting;
}
MarkOld(customer);

or

LoadProperty(customer, Customer.NameProperty, something);
MarkOld(customer);

mattruma replied on Thursday, February 03, 2011

Thanks Jonny ... I'll check that out ... in the meantime ... another question for you ... Smile

In regards to not creating something like and ICustomer object ... the reason I was thinking of doing this is so my ICustomerFactory would look like:

This way I don't have to have something like InsertCustomer(string name, string phone, string fax ... and so on) ...

 

JonnyBee replied on Thursday, February 03, 2011

Your ObjectFactory classes are responsible for creating the actual BusinessObjects and maintainig the proper state (New/Old/Clean)!!!

So your Customer object will typically have:


    public static Customer GetCustomer(long id)
    {
      return DataPortal.Fetch<Customer>(id);
    }

ant the actual ObjectFactory will have:


  public class CustomerFactory : ObjectFactoryICustomerFactory
  {
    public object Fetch(long criteria)
    {
      var customer = (Customer)MethodCaller.CreateInstance(typeof(Customer));

// load data
      LoadProperty(customer, Customer.IdProperty, idvalue);
      LoadProperty(customer, Customer,NameProperty, name);

// set the state to Old (and implicitly also Clean)
MarkOld(customer);
      return customer;
    }

 

 

mattruma replied on Thursday, February 03, 2011

So in this case, the business object is NOT calling the DAL directly ... I would like the DAL to invoke the DAL, like the following code:

    private void DataPortal_Fetch(SingleCriteria<ProductEditstring> criteria)
    {
      using (var dalFactory = DataAccess.DalFactory.GetManager())
      {
        var dal = dalFactory.GetProvider<DataAccess.IProductDal>();
        using (var dr = new SafeDataReader(dal.Fetch(criteria.Value)))
        {
          dr.Read();
          using (BypassPropertyChecks)
          {
            Id = dr.GetString("Id");
            Name = dr.GetString("Name");
          }
          ValidationRules.CheckRules();
        }
      }
    }

And instead of it returing a IDataReader, it would return an ICustomer object ... so that my BO could still handle the loading the objects.

And instead of ...

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Insert()
    {
      using (var dalFactory = DataAccess.DalFactory.GetManager())
      {
        var dal = dalFactory.GetProvider<DataAccess.IProductDal>();
        using (BypassPropertyChecks)
        {
          dal.Insert(Id, Name);
        }
      }
    }

I would have something like dal.Insert(this) where this implements ICustomer ...

I am basing this off of the Core 3.8 Demos in the the Data\DbRepos ... Is this doable with interfaces ... or should I pull the DAL out completely and use the example where Rocky uses the FactoryLoader?

Thanks again for your help ... I do appreciate it! 

 

 

 

JonnyBee replied on Thursday, February 03, 2011

My misunderstanding, I thought you were thinking of the ObjectFactory implementation in Csla.

The cslacontrib also has a sample shows a Repository pattern following your line of thinking. Have a look at the MEF Repository sample.

Have a look at my blog post too: http://jonnybekkum.wordpress.com/2010/12/30/cslacontrib-mef-and-repository-pattern-with-csla4/

mattruma replied on Thursday, February 03, 2011

Thanks Jonny ... this is getting me a lot closer! I like the MEF Repository example ... I see how it works, up to the point where the DAL is injected ... where and when does that happen? Is it determine by a configuration file?

What if I want to inject my own DAL at the point of the test with a different set of data ... is it as easy as setting the property on the business object?

JonnyBee replied on Thursday, February 03, 2011

Hi Matt,

The static IoC class will initalize the MEF container (for production) from the assemblies in the bin folder.

              //create container
              var catalog = new AggregateCatalog();
              catalog.Catalogs.Add(new DirectoryCatalog("."));
              catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
              _container = new CompositionContainer(catalog);
              _container.ComposeParts();

But for unit tests you may supply your own pre-initialized container that will
resolve interfaces into f.ex. mock-classes:

    /// <summary>
    /// Injects the container. Use this for unit testing where you want to control the
/// type resolving by supplying your own pre-initialized container.
    /// </summary>
    /// <param name="container">The container.</param>
    public static void InjectContainer(CompositionContainer container)
    {
      lock (_syncRoot)
      {
        _container = container;
      }
    }

 

mattruma replied on Thursday, February 03, 2011

Jonny ... I'm struggling a bit here ... I conceptual understand what is going on ... just having a hard time making it happen in my code ...

Let's say I have a class called CustomerTest that has some tests in it ... and for each test I want to inject a CustomerDataAccessFactory that returns a certain set of data when the Fetch is called. I basically want to new up my CustomerDataAccessFactory and tell the business object to use this DalFactory ... how would I do that?

Thanks again for helping me out ... I feel I am so close ... just missing something. Smile

JonnyBee replied on Thursday, February 03, 2011

Hi Matt,

There going to be some code but here goes:

The Business object:

  [Serializable]
  public partial class MyRoot : MefBusinessBase<MyRoot>
  {
  public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(c => c.Id);
    public int Id
    {
      get { return GetProperty(IdProperty); }
      set { SetProperty(IdProperty, value); }
    }

    public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    [Required]   // Data Annotations rule for Required field
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }

   public static MyRoot GetRoot(int id)
    {
      return DataPortal.Fetch<MyRoot>(id);
    }

    protected override void AddBusinessRules()
    {
      //// call base class implementation to add data annotation rules to BusinessRules 
      base.AddBusinessRules();
    }

// repository data access injected by MEF
    [NonSerializedNotUndoable]
    private IRootDataAccess _myRootDataAccess;

    [Import(typeof(IRootDataAccess))]
    public IRootDataAccess MyRootDataAccess
    {
      get { return _myRootDataAccess; }
      set { _myRootDataAccess = value; }
    }

    public void DataPortal_Fetch(int criteria)
    {
      var data = MyRootDataAccess.Get(criteria);
      using (BypassPropertyChecks)
      {
        Id = data.Id;
        Name = data.Name;
      }
      MarkOld();
    }
  }

IRootDataAccess and Rootdata defined like this:
  public interface IRootDataAccess
  {
    RootData Get(int id);
  }

  public class RootData
  {
    public int Id { getset; }
    public string Name { getset; }
  }

Then the unit test fake data:
  [Export(typeof(IRootDataAccess))]
  public class MyRootFakeData : IRootDataAccess
  {

    public RootData Get(int id)
    {
      if (id == 1) return new RootData() { Id = 1, Name = "Jonny" };
      if (id == 2) return new RootData() { Id = 2, Name = "Matt" };
      if (id == 999) throw new System.Data.SyntaxErrorException("Test");

      return new RootData() { Id = id, Name = string.Format("Name {0}", id) };
    }
  }

And the unit tests:
 [TestClass()]
  public class MyRootTest
  {
    private TestContext testContextInstance;
    public TestContext TestContext
    {
      get {return testContextInstance;}
      set {testContextInstance = value;}
    }

    [ClassInitialize()]
    public static void MyClassInitialize(TestContext testContext)
    {
      // set up IoC container to use MyRootFakeData
      var container = new CompositionContainer();
      container.ComposeParts(new MyRootFakeData());
      CslaContrib.MEF.Ioc.InjectContainer(container);
    }

    [ClassCleanup()]
    public static void MyClassCleanup()
    {
      CslaContrib.MEF.Ioc.InjectContainer(null);
    }

    [TestMethod()]
    public void GetRootTest()
    {
      var root1 = MyRoot.GetRoot(1);

      Assert.AreEqual(1, root1.Id);
      Assert.AreEqual("Jonny", root1.Name);

      var root2 = MyRoot.GetRoot(2);
      Assert.AreEqual(2, root2.Id);
      Assert.AreEqual("Matt", root2.Name);
    }

    [TestMethod()]
    [ExpectedException(typeof(DataPortalException))]
    public void GetRootThrowsDataPortalException()
    {
      var root1 = MyRoot.GetRoot(999);
    }
  }

So - I'd prefer to have one set of fake data access per object type and use criteria objects to load different type of data or even throw exceptions.

 

mattruma replied on Thursday, February 03, 2011

Jonny ... This worked perfectly! Thank you soooo much! Smile

ajj3085 replied on Thursday, February 03, 2011

Johnny, whats to stop a UI developer from using the IRootDataAccess property and then hitting the db directly?  I guess in three tier mode that property would be null because you wouldn't configure the interface on the client, but in two tier mode I could see that potentially being an issue.

JonnyBee replied on Friday, February 04, 2011

You could change access to the injected property to private. MEF can inject into public, internal and private properties.

If you wish to keep the property public and hidden in designers/intellisense that use your assembly you could add the EditorBrowsableAttribute to the property:

    [EditorBrowsable(EditorBrowsableState.Never)]

For more info on this attribute:
http://msdn.microsoft.com/en-us/library/system.componentmodel.editorbrowsableattribute.aspx

mattruma replied on Friday, February 04, 2011

I am working through this now in my test class and am now receiving the following error:

Type 'CslaContrib.MEF.MefBusinessBase`1[[CompanyName.ProjectName.Business.Customer, CompanyName.ProjectName.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' in Assembly 'CslaContrib.MEF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

Is it as simple to mark the MefBusinessBase as Serializable or this masking some other problem?

mattruma replied on Friday, February 04, 2011

Added the [Serializable] Attribute to the MEF objects and everything seems to work ... tests pass ... confirming right now.

JonnyBee replied on Friday, February 04, 2011

Thanks Matt.

Cant believe i missed the [Serializable] attribute.

I have added [Serializable] to all base classes in CslaContrib and correct ones are available for download now.

 

mattruma replied on Friday, February 04, 2011

Thanks again Jonny!

RockfordLhotka replied on Thursday, February 03, 2011

Tomorrow I'll have an update to the Using CSLA 4: Data Access ebook online for owners of the ebook series, and the book now includes a pretty complete discussion of exactly this topic from the data portal perspective.

There are four data access models supported by the data portal:

  1. Encapsulated invoke
  2. Factory implementation
  3. Factory invoke
  4. Encapsulated implementation

I listed these (arguably) in order from best to worst.

The reality is that there are more than these four models. People can be very creative, often adding duplicate layers of complexity and indirection. But these four are the ones that I consider when working on the data portal.

The "encapsulated" models use DataPortal_XYZ methods as in your post, and you are talking about encapsulated invoke, which is where there's an external DAL that is invoked from the DataPortal_XYZ methods.

When you design an external DAL, the primary thing you must consider is the interface exposed by the DAL. My examples tend to use either data reader or DTO interfaces, and the code that actually manipulates the business object state is in the DataPortal_XYZ method - hence not breaking encapsulation.

The "factory" models always break encapsulation, because the factory or DAL is manipulating the private state and metastate of the business objects. That's really not good, but it is pragmatic.

If you wanted, you could implement a DAL for the encapsulated invoke model, where the DAL does break encapsulation. This is like a hybrid between numbers 1 and 2 I suppose. Not an "official" model, but it would work.

mattruma replied on Thursday, February 03, 2011

Hi Rocky,

Thanks for your response ... and I look forward to the ebook update! I am leaning towards the following:

When you design an external DAL, the primary thing you must consider is the interface exposed by the DAL. My examples tend to use either data reader or DTO interfaces, and the code that actually manipulates the business object state is in the DataPortal_XYZ method - hence not breaking encapsulation.

I think I am going to use a DTO and let the business object manage the state ... where I am getting stuck at is when it comes to Mocking DAL ... I would like to have different sets of data based on the unit test I am running. Just trying to figure out the best way to handle this ... and the only thing I can come up with is someway to "inject" my DAL on the fly that will allow me to have specific data returned for the given test case.

ajj3085 replied on Thursday, February 03, 2011

mattruma
I think I am going to use a DTO and let the business object manage the state ... where I am getting stuck at is when it comes to Mocking DAL ... I would like to have different sets of data based on the unit test I am running. Just trying to figure out the best way to handle this ... and the only thing I can come up with is someway to "inject" my DAL on the fly that will allow me to have specific data returned for the given test case.

I'm doing this same thing.  Basically all DAL implemenations will reference a Data assembly, which has DTO objects where each type maps directly to a database table.  Also, there's an IRepository interface that provides access to those.  For me what I've done is my IRepository is declared like so:

public interface IRepository { IQueryable<Contact> { get; } IQueryable<Product> { get; } } and so on.

My real implemention of the repository implements that just by being a thin wrapper around a Linq 2 Sql DataContext.  For testing, my mock repository returns List<T> of the item (so it looks like this:  public class MockRepo : IRepository { IQueryable<Contact> { get { return contacts.AsQuerable(); } }

The actual injection is done via StructureMap, and my BO uses StructureMap.ObjectFactory.GetInstance<IRepository> to get the repo.  The down side is you need to spin up StructureMap on test startup.  I'm currently trying to figure out how to get around that even... I have a few ideas, but I haven't started coding yet, so we'll see...

Andy

Copyright (c) Marimer LLC