0

I'm a student and I'm learning about domain driven design. From my reading, an important part is removing the getters and setters from a class (thus keeping the implementation of that class private). My question is how can you do unit tests for this class.

Consider the following example:

public class Account {
    private Money money;

    public void withdraw(float money){
        this.money.subtract(money);
    }
}

public class Money {
    private float value;
    private Currency currency;

    public void subtract(float money) {
        this.value -= money;  
    }
}

How can I test the Account class, or event the Money class? In case of an error I can throw an exception and configure the test so that it expects that exception. What about the happy path? How can I check if the value after the method is called is the correct value?

  • 3
    If you return the remaining balance (i.e. value) from the subtract method, you can assert against that. Having a getter just to see if value is correct is ill-advised; you're breaking encapsulation at that point. – Robert Harvey Sep 8 '17 at 20:41
  • 1
    Possible duplicate of Are Get-Set methods a violation of Encapsulation? – Euphoric Sep 8 '17 at 21:07
  • 6
    "What about the happy path?" -- yes, what about it? what would be a happy path for the code snippets here, how these could be used? I can't see how - this code just doesn't make sense. Think of it, you test what you can use - if there's no use there's nothing to test – gnat Sep 8 '17 at 21:21
  • 1
    @Euphoric: Eh, not really. That's more of a "gnat swipe" duplicate, the usual (ill-advised) practice we have here of marking as a duplicate of a question that addresses some relevant, but peripheral point in an oblong way, but doesn't really address the original question. – Robert Harvey Sep 8 '17 at 21:22
  • 1
    @RobertHarvey While it is not exactly same question. It does invalidate core assumption in this question. Removing all getters and setters in name of "protecting encapsulation" is irrational idea. Problem in this question stems from adhering to that irrational idea. It is similar to XY Problem. – Euphoric Sep 8 '17 at 21:25
1

How can I test the Account class, or event the Money class?

This question has a couple of different flavors of answer.

The most straight forward answer is that we are using the domain model manage change to a data model. That Money.value came from somewhere, and the motivation for invoking Account.withdraw is to change that specific value at its source.

Given: an initial state for the source
When: the domain model is directed to perform a behavior
Then: the change to the source satisfies the specification

For state of the resource, think DTO; a representation of that state designed to cross process boundaries.

The same idea expressed another way: there must be a way to inject data into the domain mail, and extract it back out. There's something that understands how to take primitive inputs and transform them into Value Objects; and like wise there must be something that understands how to take Value Objects and convert them back into primitive outputs.

If you are familiar with TDD, you see this approach taken when designing a new class -- consider the bowling game kata. The test models rolls as int inputs, and models the score as an int output. When we start making those concepts explicit in the model, we write code to transform the primitives to the explicit concept, and vice versa.

A slightly different variation; what we are typically doing with a domain model is ensuring that changes to a book of record satisfy some business invariant. So we think in terms of passing the "book of record" to the model, let the model make the changes, and then inspect the book of record to verify the changes.

// Given
Model m = Model.from(bookOfRecord);
Command c = Command.from(message);

// When
{
    m.runCommand(c);
}

// Then
assert specification.isSatisfiedBy(bookOfRecord);

A different answer came from Greg Young, when he introduced the probability kata "test the model in the calculus of itself".

For example, the fact that, in your example, the domain model uses a floating point value to track the current state of Money is an implementation detail. It's one that is likely to change as your model gets more sophisticated about rounding rules.

That kind of a change shouldn't break all of your tests.

The "state" of the model should be a black box; what the tests should be focused on is not that the model ends up in the expected "state", but instead that the model produces the right responses to queries.

Some of those queries may be as simple as "is the model internally consistent?"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.