views:

169

answers:

3

Hi, i'm having trouble writing integration tests which use a fake repository,

For example : Suppose I have a classroom entity, which aggregates students...

var classroom = new Classroom();
classroom.Students.Add(new Student("Adam"));

_fakeRepository.Save(classroom);

_fakeRepostiory.GetAll<Student>().Where((student) => student.Name == "Adam")); 
// This query will return null...

When using my real implementation for repository (NHibernate based), the above code works (because the save operation would cascade to the student added at the previous line),

Do you know of any fake repository implementation which support this behaviour? Ideas on how to implement one myself?

Or do you have any other suggestions which could help me avoid this issue?

Thanks in advance, Erik.

+1  A: 

If you are writing "integration test" as the body of your question indicates, you wouldn't use a fake implementation -- use the real thing.

If you are writing unit tests, as the title of your question indicates, you wouldn't care whether the operation cascades, because that is not a concern of the Classroom class -- all you care about is that Save() gets called on whatever repository the class was given as a dependency.

If you are writing a unit test around NHibernate's ability to perform cascade operations, those have already been written and prove nothing about the Classroom class.

Edit in response to comment:

Unit tests are used to test individual "units" of functionality, independent of any other classes or services -- in this case, you use fakes for those other classes to ensure that they do exactly and only what this class needs. Integration tests are used to test the interaction among multiple classes and/or subsystems (like database access, ORM, etc.). In this case, it seems like you want to test the cascading save/update, but that is the responsibility of NHibernate here, isn't it?

But let's say you have a class that saves classroom using an IRepository<Classroom>, and it wraps that action in a try…catch block, and you want to unit test that when the IRepository<Classroom> throws a RepositoryWriteFailureException, an error count property, ErrorCount gets incremented. Here you do need a fake implementation of your repository, and this is where a mocking framework like RhinoMocks, nMock, Moq, or TypeMock Isolator come into play -- these can generate the fake for you, using an interface, abstract class, or a class whose public methods and properties have been declared virtual.

I might write the body of a test like this (treat this as pseudo-code):

// set up the context (arrange)

// create the fake repository
var fake_repository = MockRepository.GenerateStub<IRepository<Classroom>>();

// create the classroom, injecting the fake repository
dim classroom as new Classroom(fake_repository);

// now tell the fake how to behave
fake_repository.Stub(repo => repo.Save(classroom))
    .WhenCalled(throw new RepositoryWriteFailureException());

// do what you're testing (act)
classroom.Save(); 


// assert the expected behaviour (assert)

// verify that the fake_repository was told to Save(classroom)
Assert.IsTrue(fake_repository.WasToldTo(repo => repo.Save(classroom)).Times(1);

// verify that the error count property was incremented
Assert.IsTrue(classroom.ErrorCount == 1);

Notice how we have tested how the Classroom class, or whatever you're testing, behaves without even having to implement the repository.

Jay
Thanks for the reply jay! Sorry for mixing the terms unit and integration testing, the code above doesn't aim to test the classroom, it's a simplification, suppose i have a service which has a dependency on a generic IRepositoryTEntity>, as part of it's logic it calls : _repository.Save(SomeAggregate).The aggregate aggregates several entities in it.The NHRepositry implementation supports cascading save operations... My current fake doesn't...How would you (unit? integration? not sure about the correct term) test that service?(Sorry for my english)Erik.
Erik Ashepa
@Erik I updated the post with more details. The gist of it is that you can use a mocking framework to create the fakes, or if you roll your own, just make it return values *as if* it were doing the cascades.
Jay
Thanks jay... i am well aware to mocking frameworks... :))The signle scenario i'm having problem with is testing service classes which call save on aggregate roots (Order for example), and then later in the test, i must assert that a specific orderline was saved... sorry if i wasn't very clear...
Erik Ashepa
@Erik So… you're not mapping NHibernate to persist the collections with the aggregate root? Your repository classes have responsibility for this? If so, and that is what you are testing, then you should use the actual repository. If you actually want to test that an item was saved to the database, then you're doing an integration test and you should use an actual database (ideally something lightweight and in-memory, like Sqlite).
Jay
yes, my repositories are responsible for persisting the entire aggregate root with all of it's sub entitites...suppose some service calls:OrderRepository.Save(order), would persist the order with all of it's order lines... if later in the method there would be logic which depends on order lines then using a fake/mock/stub repository would break that logic becuase it would not cascade...Thanks for the help jay, but i guess the only way to go is sqlite (in-memory mode) + nhibernate for those scenarios, with all of the implications..
Erik Ashepa
@Erik I guess I'm wondering why not use NHibernate's built-in, well-tested, and time-proven ability to handle cascading updates? If you are testing the service that uses the repository, as you said, you *can* use a fake. In your test, you create a collection of `OrderLines` by hand, and you tell the fake repository to return that collection from the `GetOrderLines()` method or whatever, as long as `Save()` has been called. Really, though, if you have the `OrderLines` in memory to persist them, you'd never have a need to fetch them from the repository in the same method.
Jay
+1  A: 

Since it is an integration test, I'd recommend you to use the real repository, and not just a fake one.

A good solution would be to create a separate database for testing purposes and set NHibernate's hbm2ddl.auto property to create in the test project's config file, that way it would re-create the database every time, so you wouldn't have to worry about that eitther.

I use exactly this for testing purposes. I use FluentNHibernate's auto mapper with custom conventions. You may also want to use SQLite if you'd like to use something lighter that SQL Server for testing.
(I unit test my repository this way, using SQL Server.)

You can also set it to update, too, if you want it to preserve data you already entered to it, and you shouldn't set it at all if you're okay with manually creating a testing database.

Venemo
Hi, Thanks for the reply... that's currently my only alternatinve...but the problem is that i must deal with persistence (or at lease the mapping), in a very early stage, just to perform unit-testing...
Erik Ashepa
Again, there seems to be confusion between unit and integration testing. Unit testing you can do at an early stage -- you fake the repository behaviour and do not use a database. If you are ready for integration testing with the actual repository implementation, then you necessarily must have your persistence/mapping already in place and a database for testing (and wouldn't likely be at a very early stage if you build out your domain model/business logic first).
Jay
@Erik, don't let that bother you. It is a good thing to test your repository, always.
Venemo
@Jay, I couldn't agree more.
Venemo
A: 

+1 for not calling it «integration test» if the test doesn't use the real thing (i.e. isn't testing the integration with the RDBMS).

If you want cascading and all the other goodness of NHibernate and the speed of «unit test»s, I've found using an in-memory database (SQLite) extremely useful.

Martin R-L
Thanks for the reply, do you use nhibernate with and in-memory sqlite database?The only downside to this approach is that you must map your entities from the very start of the project... (even though fluent nhibernate makes it pretty easy, especially with it's auto-mapping feature)Or am i missing something?
Erik Ashepa
Yes, it means you have to map your entities from the start.Although NH, and other ORMs, claim to support POCO, thats not 100% true. So if you know you're going to use NH, I think getting the mappings right early is an ok trade-off for the somewhat lost domain focus.
Martin R-L