如何在持久性无知的情况下进行单元测试

发布于 2024-11-18 17:33:14 字数 625 浏览 2 评论 0原文

我正在开展一个新项目,在该项目中我积极尝试尊重持久的无知。例如,在我的服务层中,我从 ORM 检索一个实体,并调用在该实体上定义的例程,该例程可能会也可能不会对该实体进行更改。然后,我依靠 ORM 来检测实体是否被修改,并进行必要的插入/更新/删除。

当我运行该应用程序时,它按预期工作,并且看到它的实际效果真是太棒了。我的业务逻辑非常孤立,我的服务层非常薄。

当然,现在我正在添加单元测试,并且我注意到我无法再编写单元测试来验证某些属性是否被修改。在我之前的解决方案中,我确定是否对处于预期状态的对象进行了存储库调用。

mockRepository.Verify(mr => 
  mr.SaveOrUpdate(It.Is<MyEntity>(x => 
    x.Id == 123 && x.MyProp == "newvalue")), Times.Once());

我是否正确地对待持久性无知?当我没有显式调用存储库的保存方法时,是否有更好的方法来对实体的操作后状态进行单元测试?

如果有帮助的话,我正在使用 ASP.NET MVC 3、WCF、NHibernate 和 NUnit/Moq。我的单元测试调用我的控制器操作,传递服务类的实例(使用模拟存储库实例化)。

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 makes 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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心欲静而疯不止 2024-11-25 17:33:14

您正确地处理了这个问题,因为您有一个代表存储库的接口,并在测试中传递了一个假值,我更喜欢为我的存储库使用内存模拟器,而不是使用模拟,因为我发现存根实现倾向于使我的测试比使用模拟/验证更脆弱(根据模拟不是上面链接的存根文章)。如果您的存储库具有添加/查找/删除方法,我的内存中实现会将这些方法转发到成员列表,然后保存将设置一个名为 SavedList 的属性,我可以在测试中对其进行断言。

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.

裸钻 2024-11-25 17:33:14

奇怪的是,我偶然发现了一个解决方案,而且非常简单。

[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);
}

它并不完美,但我可以假设如果 widget.Description 与我分配给视图模型的值匹配,那么 ORM 将保存该更改,除非调用 evict 。

更新:
刚刚想出了另一个替代解决方案。我在我的基础存储库中创建了一个 ObjectStateAssertionForTests(Object obj) 函数,该函数不执行任何操作。我可以在我的代码中调用这个函数,然后在我的单元测试中检查它。

mockRepository.Verify(mr => 
  mr.ObjectStateAssertionForTests(It.Is<MyEntity>(x => 
    x.Id == 123 && x.MyProp == "newvalue")), Times.Once());

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());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文