Slow new's day - first crack at Base4 'n Castle

Base4 & Castle

Well I've been working with HTML & style sheets today (mostly) - It's be a long while since I've done web development, so it was a little tedious, and it did briefly cross my mind that life might be easier if we lived in some kind of dictatorship where style sheets and layout were provided for me by the "man"... at any rate I also managed to fit in a bit of monorail and base4 to keep my brain from freezing over.



I think for this post I might just talk about the way I'm using base4... basically I prototyped some stuff and came to the conclusion that I didn't like the smell of the static StorageContext class in base4 and it's default connection - too hard to test against - not to say that it's a bad idea, just that I couldn't see any easy way to test and mock code  for code that used/consumed it... so I decided to work up a simple alternative using castle's IoC and some generic interfaces...



At this point... If you've been using Castle for more then a couple of week this is all old news I'm sure, so probably better off finding something else to read ;o)

The facility

First off we have a facility:

public class Base4StorageFacility : AbstractFacility
{
protected override void Init()
{
string url = FacilityConfig.Attributes["url"];
if (string.IsNullOrEmpty(url))
{
throw new StorageException("The Base4StorageFacility requires a "url" attribute to be set");
}

Kernel.AddComponentInstance("base4.defaultContext", typeof (IItemContext), StorageContext.NewContext(url));
Kernel.AddComponent("base4.repository", typeof (IRepository<>), typeof (Base4Repository<>));
}
}



A facility extends the container with additional functionality, in this case the only reason we're using a facility (instead of registering the components themselves individually) is because we're using a static method to create our context for a specific base4 connection url.



Moving on from here we can register the facility in the containers configuration, incidentally now I have somewhere pleasant to configure what base4 server I connect to by default in my application.



type="MyProject.Core.Base4StorageFacility, MyProject.Core"
url="tcp://Server:@localhost:999/Base4_default" />



The Repository

Now what about the IRepository<> ? well here's the interface:

public interface IRepository
where T : class, IItem, new()
{
IItemListProxy List();
IItemList FindAll();
IItemList Find(ObjectQuery query);
IItemList Find(string path);
IItemList Find(string path, params string[] replaces);
IItemList Find(ObjectPath path);
IItemList Find(ObjectPath path, ObjectScope scope);
IItemList FindUsingSQL(string SQL);
IItemList FindUsingSQL(string SQL, ObjectScope scope);
T Get(T previous);
T Get(object id);
T Get(ItemKey key);
T Get(string relativeUri);
T FindOne(ObjectQuery query);
T FindOne(string opath);
T FindOne(string path, params string[] replaces);
T FindOne(ObjectPath path);
T FindOneUsingSQL(string SQL);
T FindOneUsingSQL(string SQL, ObjectScope scope);
T FindOne(string opath, ObjectScope scope);
T FindOne(ObjectPath path, ObjectScope scope);
void DeleteAll();
void Delete(ObjectPath path);
void Delete(string path);
void Delete(string path, params string[] replaces);
void Delete(T item);
void Save(T item);
T Create(string Xml);
T Create(XmlReader reader);
T Create();
}


It's pretty much works like IItemContext, except that you avoid having to pass generic parameters to the individual methods because the interface itself has a generic parameter... there's a couple of extras there that I'll cover at the end of this post too.



Conversely, the class Base4Repository implements this interface... which looks like this (or at least the first few methods, I've left the rest out for brevity) - this is similar to what Ivan has done.


public class Base4Repository : IRepository
where T : class, IItem, new()
{
private IItemContext _context;

public Base4Repository(IItemContext context)
{
if (context == null) throw new ArgumentNullException("context");
_context = context;
}

public virtual IItemListProxy List()
{
return _context.List();
}

public virtual IItemList FindAll()
{
return _context.FindAll();
}



Notice that it doesn't have a parameterless constructor, we rely on the container to inject the default IItemContext when creating instances of the Base4Repository...

The Container

Now, by default the container assumes a component has a "singleton" lifestyle, thankfully for a type with a
generic parameter it is per that parameter, so this test case below passes - incidentally if you tend to use lifecycles other then the default, I would strongly suggest adding tests to make sure the lifecycle is actually applied... you can just imagine what happens in a multi-threaded app when a "Message" class has a singleton lifecycle when you expected a transient ;o) you end up with some bizarre behavior that might not be picked up in normal unit tests.

[Test]
public void IsSingleton()
{
IRepository fileRepository1 = container.Resolve<>>();
IRepository fileRepository2 = container.Resolve<>>();
Assert.AreSame(fileRepository1, fileRepository2);

IRepository typeRepository1 = container.Resolve<>>();
IRepository typeRepository2 = container.Resolve<>>();
Assert.AreSame(typeRepository1, typeRepository2);
}



cool, clear as mud?



Right... so moving on from there, in my own components when I need to access data I now use the repository... so I could, for instance, create a monorail controller like this:


public class FileController : BaseController
{
private IRepository _repository;

public FileController(IRepository repository)
{
if (repository == null) throw new ArgumentNullException("repository");
_repository = repository;
}

public void Fetch(Guid fileId)
{
CancelLayout();
CancelView(); // very important
Response.Clear(); // ensure the response is empty

FileBase file = _repository.Get(fileId);
if (file != null)
{
Response.ContentType = file.MimeType;
file.FileContent.CopyTo(Response.OutputStream);
}
else
{
Response.StatusCode = 404;
}
}
}



And everything will just work - now in some cases this isn't convenient (the dependency injection model) so I broke down and
created a static repository class as well, which accesses the default container for the application... basically it has the same interface as IRepository, but they're static methods.. so you can code like this:


Track track = Repository.Get(id);



Personally I'm not actually that keen on this approach - The Syzmk RMP product I've worked on uses the container everywhere, but avoids ever having to access the default container statically...  and if you end up with a class being injected with a large number of dependencies it's often (but not always) a good indication that there's some violation of orthogonality - if only because a class consuming that many dependencies is probably doing more then one thing... a little difficult to pick up on otherwise.



But at any rate It seems pretty good for a website, where I can't see me using more the one container (or even child containers) within the same app domain.

Wrinkles...

Moving beyond that, the last thing I have to say is that originally I was creating new instances and then saving them with a repository like this:

Group group = new Group();
...Repository.Save(group);
...
User user = new User();
...user.Groups.Add(group);
...Repository.Save(user);


However, it all turns to 4 shades of brown when we go to add to a many-to-many collection, like this:


Goat goat = new Goat();
goat.Name = "junk";
Repository.Save(goat);

BoatOfGoats boat = new BoatOfGoats();
boat.Name = "track";
boat.Goats.Add(goat); // <-- "storageexception="" :="" default="" is="" not="" available,="" as="" no="" default="" has="" been="">
Repository.Save(track);



As far as I can tell the event handling for the Add(item) method expects a default context to be assigned... now I'd be tempted to call this a bug, but that's a bit rash till I understand the in's and out's of base4, however there's an easy way round it... and that's to assign the context yourself :) so a fix would be to add this line before the one that bombs:


track.Context = container.Resolve();


But that's pretty kludgey, so instead of using:


Goat goat = new Goat();


I added some Create(...) overloads to the repository to take care of it... and do this:


Genre genre = Repository.Create();

Last of all, though I haven't drawn on the entire implementation, I'm hoping to follow (At least in spirit) some of the work Ayende has done with his NHibernate repository concept as the project progresses, I think it will add a more natural "feel" combined with the repositories for implementing transactions vs. interacting with base4's ObjectTransaction directly, not to mention providing something I can test and mock out easily...  We shall see what actually happens as the project progresses.

Read More

Hail...

Just hailed like hell here, thought a window was going to break ;o)

Read More

The latest drop of base4

Well, I've started working with the latest drop of Base4.net (version 2.1) - in fact I've decided to take the plunge and actually start using it for the project I'm currently working on (a work project, rather then a home project) so I should be giving it a pretty good thrashing over the next 3-6 months.  I already had a rough schema in place, so I generated types from it using the bundled web admin... It seems pretty cool... though I've struck a few things:

  • nvarchar(256) fields seems to show up as being 512 characters in length within base4.net...  I'm not sure if this is a bug with the schema interrogation, guess we'll find out as a I dig further in ;o) - though it's only skin deep of course, as you can change them to 256 with a quick edit.

  • I managed to create an assembly that was invalid... not sure how ;o) I could look at in reflector .Net, but base4 wasn't having a bar of it (even though it had generated it) - I must have screwed up a relationship somewhere, but I wasn't really sure... ended up just dumping it and starting from scratch again.

  • It doesn't seem that easy to refactor existing schema's using the web designer... while I was learning it would have been good to generate a schema, play with the assembly, then start editing and generate again... instead I ended up having to save the schema to disk, delete, edit, and recreate.  Still, it's pretty slick :)


I think though it probably all comes down to:
  • My urge to make things happen without understanding the in's and outs of base4.net
  • Horses for courses, the web admin is great for getting off to ground with base4.net, but I think I'll have to get down and dirty with the xml pretty soon...


Though I can't discuss the project, I can discuss what I'll be building it in... so we have:
  • Base4.Net
  • SQL 2005
  • Castle (Monorail & IoC)
  • NLog (or log4net... though I'm loosing faith in the latter)
  • NUnit
  • RhinoMocks
  • Splicer.Net
  • Some COM & Unmanaged code for audio processing

This is also the first time I've used monorail for a commercial project (we already use a lot of the facilities and the windsor container in the Syzmk RMP product) so it's certainly some interesting times... as we progress I'll give some feedback on both the Monorail and Base4.Net "user experience" :)
Read More

Sourcode for "Integrating NUnit & IronPython"

Back again!


Here is the source for the last post: ironpython-and-nunit.zip (23.01 KB) - it's pretty rough around the edges (and in the middle :P) because I was just trying to see if it was possible, rather then to produce some production-level toolkit for integrating IronPython and NUnit.



Edit 2008-03-14: An updated version of the code target
NUnit 2.4 and VS2008 can be found here.



Usage

First off... usage is pretty straightforward.
  • Test fixtures must have NUnitFixture as one of their base classes.
  • Test methods must be prefixed with "test".
  • The setup method, if you need one, must be called "setUp"
  • The teardown method, if you need one, must be called "tearDown"
  • Fixture setup and teardown methods are also supported, they are called "setUpFixture" and "tearDownFixture".
  • Assert.XXX methods are the same as NUnit, unless you want to assert an exception is thrown.
  • For asserting an exception is thrown, make a call to "self.failUnlessRaises(...)" - it has the same syntax as the PyUnit equivalent, however it's tweaked slightly so it will work with Python exceptions, or CLR exceptions.
  • Do not perform an "Import * from System" in your test fixtures - it will clober the native Python Exception type, and may give you odd behaviour.




Implementation

There are 8 classes and one python script which make up the implementation, the 8 classes are split between the "model"
(basically the python-side of the equation and the "NUnit extension" which are the necessary extensions to expose the model to NUnit...) - let's run through it quickly:



First we have the AbstractPythonSuite which hosts the python engine, and handles configuring and running the scripts containing the test fixtures.  Then we have a couple of classes which form the "model" consisting of a PythonFixture, representing a fixture, and PythonTestCase, representing a single test method in the fixture.



To build the model we have a PythonFixtureBuilder which is passed the python engine (from within the AbstractPythonSuite) and spits out all the fixture models, with the test cases assigned... this also assigns the various delegates for the setup/teardown/setupFixture/teardownFixture methods.



At this point the model is complete, it's just a matter of integrating it with NUnit - for this we have some more classes.



First of we need the PythonSuiteExtension, which represents the overall test suite, this contains PythonFixtureExtension's, for the fixtures, and finally PythonTestMethod for the individual test methods - the python extensions take care of running the tests in the model and calling the setup and tear down methods at the right times, and recording the results of course.



Last of all we have the PythonSuiteExtensionBuilder as mentioned in the last post, which takes care of creating the PythonSuiteExtension's for each class derived from AbstractPythonSuite it discovers in the target assembly.



Last of all we have a small python script which provides the necessary implementation for the base NUnitFixture class that all test fixtures are derived from... and a helper method we use in the PythonFixtureBuilder to identify the test methods in a class.


class NUnitFixture:
def __init__(self):
pass
def setUp(self):
pass
def tearDown(self):
pass
def setUpFixture(self):
pass
def tearDownFixture(self):
pass
def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
if issubclass(excClass, System.Exception):
try:
callableObj(*args, **kwargs)

except Exception, e:

if hasattr(e, "clsException") and
(type(e.clsException) == excClass):
return
else:
try:
callableObj(*args, **kwargs)
except excClass:
return
if hasattr(excClass,'__name__'):
excName = excClass.__name__
else:

excName = str(excClass)

raise AssertionException("%s not raised" % excName)

def getTestCaseNames(testCaseClass, prefix):
def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=prefix):
return attrname.startswith(prefix) and callable(getattr(testCaseClass, attrname))
testFnNames = filter(isTestMethod, dir(testCaseClass))
for baseclass in testCaseClass.__bases__:
for testFnName in getTestCaseNames(baseclass, prefix):
if testFnName not in testFnNames:
testFnNames.append(testFnName)
testFnNames.sort(cmp)
return testFnNames



And that's all there is to it...  If anyone actually finds this useful and wants to build on from it, please feel free, and if at all possible keep it open source (though you don't have to...)



Something to keep in mind is that at the moment if your test fixture can't actually be parsed and executed by the python engine, that exception will bubble up, eventually stopping the test assembly from being loaded at all... I think the nicer solution to this would be to add a custom NUnit test case for each script that failed to load, which in turn fails when executed as part of the assembly - dumping out the exception raised during construction.



At least then a tool like TestDriven.Net would quickly notify the developer of their mistake, instead of just falling over.



And very last of all, though I've been manually specifying the names of the script files.. using a class declaration like this:


public class MyPythonSuite : AbstractPythonSuite
{
public MyPythonSuite()
: base("MyPythonFixture.py")
{
}
}


You could make it a little more dynamic and do something like this:


public class DynamicPythonSuite : AbstractPythonSuite
{
public DynamicPythonSuite()
: base(FindSuitablePythonScripts())
{
}

private static string[] FindSuitablePythonScripts()
{
List scripts = new List();
Assembly assembly = typeof (DynamicPythonSuite).Assembly;
string indication = "#test";
foreach (string potentialScript in assembly.GetManifestResourceNames())
{
using (StreamReader reader =
new StreamReader(assembly.GetManifestResourceStream(potentialScript)))
{
if (reader.ReadLine().Trim().StartsWith(indication,
true, CultureInfo.InvariantCulture))
{
scripts.Add(potentialScript);
}
}
}
return scripts.ToArray();
}
}



Which will load all scripts with "#test" in the first line, which seem'is a little easier to me (not having to spell out the path to the script, and only having to create the test file to make things happen) - YMMV of course.
Read More

Integrating NUnit & IronPython...

Breaking scripts

I've been thinking about the issues involved with using
IronPython in a product as a scripting language, internally we've
already struggled at Syzmk with my love of refactoring breaking
scripts in our deployed products left right and centre.

I think the problems I need to solve first are:

  • Ensuring an understanding of the user experience when
    scripting with the application (read: dogfooding the scripting
    experience)

  • Making sure I identify where helper classes / wrappers etc.
    may be necessary to make the scripting experience more palatable
    to end users.
  • Ensuring any changes don't clobber the contracts between the
    scripting language and the client when refactoring the underlying
    .Net classes they rely on.

Regression tests for expected script usage is what it comes
down... but if you sit down and start trying to write these tests
in NUnit it all gets a bit unwieldy - there's a lot of time
wasted making sure your tabs are right, escaping characters
correctly in strings, and running scripts, evaluating the
results, and then asserting against them - it's frustrating, and
fails to emulate the experience of your users who are writing
scripts purely in Python.

PyUnit?

So my next port of call was to try the unit testing support that
comes with python (PyUnit) - it would definitely do the job - but
then I need to do a couple of things... first I need to start
bundling quite a few standard libraries with the scripting engine
in our product, and then I have to write additional functionality
to support capturing the results of running the PyUnit tests and
presenting them to the automated build process... It's all
sounding like a pain in the ass, and  the automation build
works nicely as it is, I'd rather not have to mess with it...

So I decided to do a little integration work, to see if I could
run a test suite written in python as part of a C# assembly.

NUnit meets (Iron)Python

First off, my solutions user experience is ok - not perfect - but
then I just wanted to make sure I could get it working, before I
put any effort into cleaning it up, and not being an expert in
extending NUnit, or coding python, it was probably doomed from
the start to be a mediocre solution ;o)

The steps to get it working in a test assembly are:

  1. Writing a fixture in python, and including it in the assembly
    as an embedded resource.
  2. Creating a suite which references the embedded python files -
    this is a class derived from the "AbstractPythonSuite".

  3. Creating a Suite builder (I had to do this in the test
    assembly itself, because it wasn't working when I bundled this in
    a support library).
  4. Run the suite ;o)

First off, we write the fixture, the only requirements are:

  • The fixture class is derived from "NUnitFixture".
  • The test methods are prefixed with "test" - as per PyUnit.
    And take only a "self" argument.

Here's an example fixture:

import System
from System import Console

class MyPythonFixture(NUnitFixture):

def testPass(self):
Console.WriteLine("--testPass--")
Assert.IsTrue(True)

def testFail(self):
Console.WriteLine("--testFail--")
Assert.IsTrue(False, "this will fail")

As you can see we are just using the existing NUnit Assert
methods... There is one caveat to this that I will cover in
another post (that of testing for expected exceptions).

Next we create a suite - which can wrap one or more python script
files (included as assembly resources) - this is all we need to
bridge the gap.

public class MyPythonSuite : AbstractPythonSuite
{
public MyPythonSuite()
: base("MyPythonFixture.py")
{
}
}

And last of all, we need to drop in a suite builder - in theory I
should be able to include this in a support assembly - but it
doesn't seem to work, here's the code:

[SuiteBuilder]
public class PythonSuiteExtensionBuilder : ISuiteBuilder
{
public TestSuite BuildFrom(Type type, int assemblyKey)
{
if (CanBuildFrom(type)) return new PythonSuiteExtension(type, assemblyKey);
return null;
}

public bool CanBuildFrom(Type type)
{
return (typeof(AbstractPythonSuite).IsAssignableFrom(type) && (type != typeof(AbstractPythonSuite)));
}
}

At this point when can test the assembly with NUnitGui,
and see our python fixtures appear under the "MyPythonSuite"...
pretty slick huh?


border="0" />



The big downsides of this approach are that you can't run the
tests individually using something like TestDriven.Net,
and Resharper's unit test window doesn't pick them up...
However NUnitGui and the corresponding NUnitConsole
have no problems with them, and running all the tests in your
assembly with TestDriven.Net does pick them up thankfully
- so as long you don't have many slow tests in your assembly it
should still be tolerable (you can run them while still
coding...).

And of course, they will be picked up in your automated builds -
which is great news for me... I hate messing around with
CruiseControl.Net's configuration.

I'll tidy up the implementation a little and post the code
soon.

Read More