remi bourgarel
remi bourgarel

Reputation: 9389

How to properly unit test a domain service?

I have a domain service

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

Here , the methods

So what will my unit test for BlobService look like ?

?

Upvotes: 0

Views: 1061

Answers (2)

Josh Kodroff
Josh Kodroff

Reputation: 28121

These are some relevant DDD testing principles I've learned along the way:

  • The only thing you need to test is behavior, not state. (This is supported by Udi Dahan if you check out how NServiceBus handler/saga tests work.)
  • Fast-running tests are critical. Slow-running integration tests are not worth the effort because they are slow and easily prone to breakage. The effort to maintain wasn't worth the return for my project.

(Again, these are specific to my project - YMMV.)

So with that in mind, here's how I would test your class. (Note that I have killed the static DomainEvents class and replaced it with IBus because it makes testing way easier in my experience.):

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

Upvotes: 0

Bartłomiej Szypelow
Bartłomiej Szypelow

Reputation: 2121

I see no value in testing if repo methods are called. Using this feature of mocks is useful when we test a component that is intended to interact with (be a wrapper of) some external system. This is not the case here.

Tests, whatever type you use, should have AAA structure. Arrange - prepare some test data/state. Act - call your service. Assert - verify the outcome (final state) is correct. You use domain events - this means testing also if correct events were raised. If you used Event Sourcing - you would do arrange part as playing events and assert part would contain only checking events raised.

Integration test are slower then unit test. You need them if your persistence leaks into the domain or contrary. For example, when you rely on lazy loading features of ORM (which is not very DDD-like by the way) or use stored procedures.

Well, don't assume too quickly that it doesn't leak ;)

Upvotes: 1

Related Questions