top of page

Testing and Domain Driven Design - a powerful combination!


Earlier this week I coached-in on a DDD session with Tactical DDD as primary objective. Tactical as in focus on Aggregates, Entities, ValueObjects and so forth, and the question: "How do we test (our code) when doing DDD?" came up.

Awesome opportunity because testing code, when doing DDD, is just satisfying. Its simplicity means we can basically do White Box Unit Testing.

Having an Onion Architecture in place for the Domain is kind of a must, as it enables us to focus on Business Rules and ignore Load & Store state issues.

Secondly - use a functional programming style, meaning, always return "something" (avoid void), and make that "something" be an Either of Type Error or Event.



Warehouse example


In a Warehouse we could have Section where we store Goods. Goods are ValueObjects with a bunch of physical attributes and some Identifier like EAN number. For simplicity we say Good only have Weight and ID .

A Section has a Max Weight (rule to test), the Warehouse do not allow Section Duplicates (rule to test).

Section is an Entity and Warehouse is an Entity Aggregate (root). On the Root Aggregate we expose:


 

fun addSection( id: SectionID ) : Either<Error, Event>

fun addGoodsToSection( id: SectionID, goods: Goods) : Either<Error, Event>


Now our Unit Tests all have this simple pattern (code is intentionally compacted to keep it short):

@Test

// Given: No Sections, adding Section should produce "SectionAdded"

val warehouse = WareHouse()

val section = Section( SectionID("mySectionID"), maxWeight = 20)

val expectEvent : Either<Error, SectionAdded> = Either.right( SectionAdded("mySectionID"))

// When

var actual : Either<Error, SectionAdded> = warehouse.addSection(section)

// Then

assert( expectEvent, actual )


// Given: Adding existing Section should produce "Error"

val expectError : Either<Error, SectionAdded> = Either.left( Error("mySectionID already exist"))

// When

actual = warehouse.addSection(sectionId)

// Then

assert( expectError, actual )


@Test

// Given empty Section with Max 20, adding Goods of weight 30 should result in Error

val warehouse = WareHouse()

val section = Section( SectionID("aSectionID"), maxWeight = 20)

warehouse.addSection(sectionId)

val goods = Goods("#EAN:0123456789", 30 )

val expectError : Either<Error, GoodsAdded> = Either.Left( Error("MaxWeightWouldBeExceed"))

// When

val actual = warehouse.addGoodsToSection(section.id, goods)

// Then

assert( expectError, actual )


 

Simple testing

Because we always get a Response and we do not need to deal with loading/storing of Data, each test becomes simple. We can even (maybe not the best practices), do continuous test flow in one go as shown in the first Test.

How to load and store Aggregates (populate the Object graph with Data) is beyond the scope here, but as always, if you have any question or are curious how DDD can help you, feel free to send a message and I will get back to you.

0 comments

Recent Posts

See All

Comments


bottom of page