Mock / stub CSLAEx safeDataReader class

Miguel’s CSLAEx provides a very good DAL for CSLA BL. The DataAccess class wraps all the db calls then make data portal function in BL much simpler.

public SafeDataReader ProductGetById(Guid productID)
{
IDataParameter[] parameters =
{
GetParameter("@p_ProductID", productID) as IDataParameter
};

return GetReader("ProductGetById", CommandType.StoredProcedure, parameters);
}

So the BO data portal becomes to this:

protected void DataPortal_Fetch(IdCriteria<Guid, Product> crit)
{
using( SafeDataReader sd =
new DataAccess().ProductGetById(crit.ID))
{
if (sd.Read())
{
_ProductID = sd.GetGuid(sd.GetOrdinal("ProductID"));
_ProductName = sd.GetString(sd.GetOrdinal("ProductName"));
_Description = sd.GetString(sd.GetOrdinal("Description"));
}
}
}

I am very pleased to see he has the same motion to move those sql plumbing code out of BO, but, why do we still see SafeDataReader here. The worse thing is, we have to call SafeDataReader.Read() again to fetch the real data.

I made some modifications then:

public class ProductRepository
{
private DataAccess _DAO = null;

public ProductRepository() : this(new DataAccess()) { }

public ProductRepository(DataAccess dao)
{
_DAO = dao;
}

public ProductDTO GetById(Guid productID)
{

ProductDTO p = new ProductDTO();

SafeDataReader sd = _DAO.ProductGetById(productID);
if (sd.Read())
{

p.ProductID = sd.GetGuid("ProductID");
p.ProductName = sd.GetString("ProductName");
p.Description = sd.GetString("Description");

}

return p;
}

[Serializable()]
public class ProductDTO
{
public Guid ProductID = Guid.Empty;
public string ProductName = String.Empty;
public string Description = String.Empty;
public string DownloadFile = String.Empty;
public decimal UnitPrice = 0;

public ProductDTO(){}

}

public class Product : BusinessBase<Product>
{
private Product() { }

protected override object GetIdValue()
{
return _Data.ProductID;
}

private ProductDTO _Data = new ProductDTO();

[DataObjectField(true, true)]
public Guid ProductID
{
get
{
CanReadProperty("ProductID", true);
return _Data.ProductID;
}
}

public string ProductName
{

get
{
CanReadProperty("ProductName", true);
return _Data.ProductName;
}

set
{
CanWriteProperty("ProductName", true);
if (value != _Data.ProductName)
{
_Data.ProductName = value;
PropertyHasChanged("ProductName");
}
}
}
....
protected void DataPortal_Fetch(IdCriteria<Guid, Product> crit)
{
_Data = new ProductRepository().GetById(crit.ID);
}

protected override void DataPortal_Insert()
{
new ProductRepository().Insert(_Data);
}

protected override void DataPortal_Update()
{
new ProductRepository().Update(_Data);
}

The benefit of this change is, ProductRepository is testable, and injectable. By switching DataAccess class to Linq2Sql DataContext class (using IDataAccess instead), the Repository and BO class won’t need to change too much.

I am disappointed he has less interest in TDD, including DDD. Why do we need this Command Object in CSLA? Can Domain object solve those sql query in a much simpiler way?

Have not learn CSLA’s parent-child yet, but I already got a feeling, CSLA is not good at relationship.

Validation is great, rules can be in 3 different levels, Information, Warning and Error. Also can be stopped by setting a flag. What a thoughtful business framework!

Tried to test those data methods.

[TestFixture]
public class ProductRepositoryTest
{
[Test]
public void GetProductOnBOTestWithoutMock()
{
Product obj = Product.GetProduct(new Guid("309e8408-2d22-45f9-82aa-01a7884c0603"));

Assert.AreEqual(obj.ProductName, "HP Laptop");
}

[Test]
public void GetByIdTestOnRepositoryWithoutMock()
{
ProductRepository r = new ProductRepository(new DataAccess());

ProductDTO p = r.GetById(new Guid("309e8408-2d22-45f9-82aa-01a7884c0603"));

Assert.AreEqual(p.ProductName, "HP Laptop");
}

[Test]
public void GetByIdTestOnRepositoryUsingMock()
{
MockRepository mocks = new MockRepository();
ITrainingDataAccess mockDao = (ITrainingDataAccess)mocks.DynamicMock<ITrainingDataAccess>();

ProductRepository r = new ProductRepository(mockDao);

Guid id = new Guid("309e8408-2d22-45f9-82aa-01a7884c0603");
SafeDataReader sd = (SafeDataReader) mocks.DynamicMock(typeof(SafeDataReader));

using (mocks.Record())
{
Expect
.Call(mockDao.ProductGetById(id))
.Return(sd);
}// Replay and validate interaction
ProductDTO p;
using (mocks.Playback())
{
p = r.GetById(id);

}// Post-interaction assertions
Assert.IsNull(p);

}

The first two are not using mock, passed based on the correct data. The last mock test failed, because the somewhere the proxy object stopped me.

System.MissingMethodException: Constructor on type ‘SafeDataReaderProxyf51d8e1fdc964a8d9baa34c2a4592667′ not found.

I am pretty sure DataReader doesn’t have a construtor, so I tried to changed repository to use ISafeDataReader from SafeDataReader, but there is no ISafeDataReader, google says there is a very similar case in subsonic world, but lucky they are using Interface.

Maybe for CSLAEx we have to use TypeMock to “Mock” SafeDataReader.

Wait a minute, here comes Phil Haack’s StubDataReader, the solution is dirty but easy:


         [Test]
        public void GetByIdTestOnRepositoryUsingMockAndStub()
        {
            Guid id = Guid.NewGuid();

            MockRepository mocks = new MockRepository();
            ITrainingDataAccess mockDao = (ITrainingDataAccess)mocks.DynamicMock<ITrainingDataAccess>();

            ProductRepository r = new ProductRepository(mockDao);

            using (mocks.Record())
            {
                //IDataReader dr = (IDataReader) mocks.CreateMock<IDataReader>();
                StubResultSet resultSet
= new StubResultSet("ProductID", "ProductName", "Description", "UnitPrice", "DownloadFile");

                decimal price = 10m;
                resultSet.AddRow(Guid.NewGuid(), "Stub prod", "some desc", price, "1.img");
                IDataReader dr = new StubDataReader(resultSet);

                SafeDataReader sd = new SafeDataReader(dr);

                Expect.Call(mockDao.ProductGetById(id)).Return(sd);

            }// Replay and validate interaction
            ProductDTO p = null;
            using (mocks.Playback())
            {
                p = r.GetById(id);

            }// Post-interaction assertions
            Assert.IsNotNull(p);
            Assert.AreEqual("Stub prod", p.ProductName);
            Assert.AreEqual("some desc", p.Description);
            Assert.AreEqual("1.img", p.DownloadFile);
            Assert.AreEqual(10m, p.UnitPrice);

        }

Update: (2008-07-16)
I used a internal DTO in my BO here which I think is not right, by doing this BO is tightly coupled to DTO. I then changed to mapper class later. Here is a similar idea about using DTO in CSLA from ASP forum.

About these ads

2 thoughts on “Mock / stub CSLAEx safeDataReader class

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s