I'm working on a new project where I'm actively trying to honor persistence ignorance. As an example, in my service layer I retrieve an entity from my ORM and I call a routine defined on the entity that may or may not make changes to the entity. Then I rely on my ORM to detect whether or not the entity was modified and it mak开发者_运维技巧es the necessary inserts/updates/deletes.
When I run the application it works as intended and it's really neat to see this in action. My business logic is very isolated and my service layer is really thin.
Of course, now I'm adding unit tests and I have noticed that i can no longer write unit tests that verify whether or not certain properties were modified. In my previous solution, I determine whether or not a repository call was made with the object in its expected state.
mockRepository.Verify(mr =>
mr.SaveOrUpdate(It.Is<MyEntity>(x =>
x.Id == 123 && x.MyProp == "newvalue")), Times.Once());
Am I approaching persistence ignorance correctly? Is there a better way to unit test the post-operational state of my entities when I don't explicitly call the repository's save method?
If it helps, I'm using ASP.NET MVC 3, WCF, NHibernate, and NUnit/Moq. My unit tests make calls to my controller actions passing instances of my service classes (which are instantiated with mocked repositories).
You are approaching this correctly in that you have an interface representing your repository and passing in a fake in your tests, I prefer to use an in-memory simulator for my repositories instead of using mocks because I find that stub implementations tend to make my tests less brittle than using mock/verify (as per the mocks aren't stubs article linked above). If your repository has Add/Find/Delete methods, my in-memory implementation will forward those to a member list and then save will set a property called SavedList that I can assert on in my tests.
Oddly enough I just stumbled upon a solution and it is really simple.
[Test]
public void Verify_Widget_Description_Is_Updated()
{
// arrange
var widget = new Widget { };
mockWidgetRepo.Setup(mr => mr.GetWidget()).returns(widget);
var viewModel = new WidgetVM { };
viewModel.Description = "New Desc";
// act
var result = (ViewResult)controller.UpdateWidget(viewModel);
// assert
Assert.AreEqual("New Desc", widget.Description);
}
It's not perfect, but I can assume that if widget.Description matches the value I assigned to my view model, then the ORM would save that change unless evict was called.
UPDATE: Just came up with another alternative solution. I created a ObjectStateAssertionForTests(Object obj) function in my base repository that does nothing. I can call this function in my code and then check it in my unit tests.
mockRepository.Verify(mr =>
mr.ObjectStateAssertionForTests(It.Is<MyEntity>(x =>
x.Id == 123 && x.MyProp == "newvalue")), Times.Once());
精彩评论