如何正确单元测试域名服务?

时间:2014-03-13 13:45:51

标签: unit-testing domain-driven-design

我有域名服务

public class BlobService{
    private FooRepository repo;
    public BlobService(FooRepository repo){
        this.repo = repo;
    }
    public void DoSomething(int id1,int id2){
        var foo1 = repo.Get(id1);
        var foo2 = repo.Get(id2);
        var valueSomething = foo1.GetValueSomething();
        foo2.SetValueSomething(valueSomething );
        repo.Save(foo2);
        DomainEvent.Raise(new ValueSomethingChanged(foo2));
    }
}

这里,方法

  • repo.Get,repo.Save通过集成测试进行测试(因为后面有一个rdbms)
  • 通过单元测试获取GetValueSomething,SetValueSomething和Raise

那么我的单元测试BlobService会是什么样的?

  • 调用repo,foo1和foo2并检查好方法。 DomainEvent是静态的,但非常简单
  • 执行集成测试:发送repo的实际实现,请求它并检查foo2值。
  • 别的东西

2 个答案:

答案 0 :(得分:1)

如果调用repo方法,我认为测试没有价值。当我们测试一个旨在与某个外部系统进行交互(成为其包装)的组件时,使用这个模拟功能非常有用。这不是这种情况。

测试,无论您使用何种类型,都应具有AAA结构。安排 - 准备一些测试数据/状态。行动 - 致电您的服务。断言 - 验证结果(最终状态)是否正确。您使用域事件 - 这意味着还要测试是否引发了正确的事件。如果您使用事件采购 - 您可以将部分安排为播放事件,并且断言部分仅包含检查事件。

集成测试比单元测试慢。如果持久性泄漏到域中或相反,则需要它们。例如,当您依赖ORM的延迟加载功能(顺便说一下,它不是非常类似DDD)或使用存储过程时。

好吧,不要假设它没有泄漏;)

答案 1 :(得分:0)

这些是我在此过程中学到的一些相关的DDD测试原则:

  • 您唯一需要测试的是行为,而不是状态。(如果您查看NServiceBus处理程序/ saga测试的工作方式,Udi Dahan会支持此。)
  • 快速运行测试至关重要。慢速运行的集成测试不值得付出努力,因为它们很慢且很容易破损。维护的努力不值得我的项目回报。

(同样,这些特定于我的项目 - YMMV。)

所以考虑到这一点,这就是我如何测试你的课程。 (请注意,我已经杀死了静态DomainEvents类,并将其替换为IBus,因为根据我的经验,它使测试更容易。):

public class BlobService
{
    private IBus _bus;
    private IFooRepository _repo;

    public BlobService(IBus bus, IFooRepository repo)
    {
        _bus = bus;
        _repo = repo;
    }

    public void DoSomething(int id1, int id2)
    {
        var foo1 = _repo.Get(id1);
        var foo2 = _repo.Get(id2);
        var valueSomething = foo1.GetValueSomething();
        foo2.SetValueSomething(valueSomething);
        _repo.Save(foo2);

        _bus.Publish(new ValueSomethingChanged(foo2));
    }
}

[TestFixture]
public class BlobRepositoryTests
{
    [Test]
    public void DoSomething_RaisesValueSomethingChanged()
    {
        // Arrange:
        var bus = new MockBus();  // just stores the events in a publicly accessible list
        var repo = new MockBlobRepository(); // does whatever you need it to do (or use Moq, etc)
        var service = new BlobService(bus, repo);

        // Act:
        service.DoSomething(1, 2);

        // Assert:
        var @event = bus.Events.OfType<ValueSomethingChanged>().Single();
        Assert.That(() => @event.Property1, Is.EqualTo(someExpectedValue));
        Assert.That(() => @event.Property2, Is.EqualTo(someOtherExpectedValue));
    }
}