Java – Unit testing when you have no getters and setters

domain-driven-designjavaunit testing

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?

Best Answer

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?"

Related Topic