I need to test lazy loaded collection to see if its returning the correct items. The problem I have is this collection uses the following code (which I'm not sure it is correct, as its my first attempt)
public static readonly PropertyInfo<ImageList> ImagesProperty = RegisterProperty<ImageList>(c => c.Images);
public ImageList Images
{
get
{
if (!FieldManager.FieldExists(ImagesProperty))
if (this.IsNew)
LoadProperty(ImagesProperty, ImageList.CreateAsChild());
else
{
ImageList.BeginGetByMajorEntityId(Id, (s, e) =>
{
if (e.Error == null)
LoadProperty(ImagesProperty, e.Object);
else
throw e.Error;
});
}
return GetProperty(ImagesProperty);
}
set { SetProperty(ImagesProperty, value); }
}
It is attempting an async load of the collection. How do I test this using nUnit to see if its working?
Using Csla 4.0 - WPF client.
Regards
Kevin
I think UnitDriven may help: http://unitdriven.codeplex.com/
Exactly. You need some scheme or framework that allows for the async operation to run, but the test itself to be "suspended" until the result comes back. There are various solutions out there. We created UnitDriven specifically so we could use the same technique in .NET and Silverlight (and now WP7), which is something (to my knowledge) nobody else enables.
The other thing you need is an event that is raised when the async operation is complete. For a lazy load, that's probably the PropertyChanged event from the object, which should be raised when the load is complete so the UI knows to refresh - or in this case so your test knows it is done.
Thanks for the replies.
I have grabbed UnitDriven and created a test. I'm having a problem where the RunWorkerCompleted is being fired before the factory has loaded the collection. I'm pretty sure its because of the async Begin inside the property getter.
How can I wait for the thread inside the worker thread (if that makes sense)?
Regards
Kevin
The test:
[Test]
public void can_get_images_lazily()
{
UnitTestContext context = GetContext();
BackgroundWorker worker = new BackgroundWorker();
var loc = Location.GetById(savedLocation.Id);
worker.DoWork += (o, e) =>
{
e.Result = loc.Images.Count;
};
worker.RunWorkerCompleted += (o, e) =>
{
int expected = 10;
int actual = (int)e.Result;
context.Assert.IsNull(e.Error);
context.Assert.AreEqual(actual, expected);
context.Assert.Success();
};
worker.RunWorkerAsync();
context.Complete();
}
I think you need to call OnPropertyChanged to notify the UI and test code that the value has changed.
You are also combining Lazy loading and Async loading.
public static readonly PropertyInfo<ImageList> ImagesProperty = RegisterProperty<ImageList>(c => c.Images);
public ImageList Images
{
get
{
if (!FieldManager.FieldExists(ImagesProperty))
{
if (this.IsNew)
{
LoadProperty(ImagesProperty, ImageList.CreateAsChild());
}
else
{
// Start async
ImageList.BeginGetByMajorEntityId(Id, (s, e) =>
{
// callback when async operation is completed
if (e.Error == null)
{
LoadProperty(ImagesProperty, e.Object);
// notify UI / test code that the value has been updated and needs to be refreshed
OnPropertyChanged(ImagesProperty);
}
else
throw e.Error;
});
// send null back to caller - will be refreshed when property is loaded.
return null;
}
}
return GetProperty(ImagesProperty);
}
set { SetProperty(ImagesProperty, value); }
}
For each object that is !New the property will return null and the when async operation is complete - load the actual
object and raise the event OnPropertyChanged to notify UI/TestCode that the value has changed.
HINT: You sould also specify ReleationshipTypes.LazyLoad on the RegisterProperty - then GetProperty will throw an
error if you try to get value before it has been loaded.
Thanks Johnny
I have put that OnPropertyChanged into my property, however how do I tell the test to wait for the second inner thread? It just completes before it has had the chance to fill the collection, with an "object reference not set..." error when checking Images property inside DoWork?
Regards
Kevin
Ah, I've got it, I think.
[Test]
public void can_get_images_lazily()
{
UnitTestContext context = GetContext();
BackgroundWorker worker = new BackgroundWorker();
var loc = Location.GetById(Location.Id);
loc.PropertyChanged += (o, e) =>
{
if (e.PropertyName == "Images")
{
context.Assert.AreEqual(loc.Images.Count, 10);
context.Assert.Success();
}
};
worker.DoWork += (o, e) =>
{
e.Result = loc.Images.Count;
};
worker.RunWorkerAsync();
context.Complete();
}
Thanks for the help guys
One final problem, now that I'm using OnPropertyChanged how do I inform the test/ui of an error? The exception is being thrown on a different thread.
Kevin
UnitDriven has a Try() method that you can use to wrap the processing to handle such exceptions.
Look at the CSLA unit tests - in particular the SL ones, but some .NET ones too - we have quite a lot of async tests you can use for inspiration.
Copyright (c) Marimer LLC