Testing the Agilogy Way

Jordi Pradel, February 2, 2024

We at Agilogy love our craft. And we like having technical discussions, trainings and workshops that allow us to continuously improve the way we develop software. That’s the reason we devote part of our Friday’s mornings to what we call the Agilogy School. These sessions are the weekly occasion for everyone at Agilogy to share and learn about common interests like testing, programming languages, functional programming, software architecture, design, etc.

As the main facilitator, I have decided to make 2024 the year of software testing at the Agilogy School.

To do so, we needed a common starting point, for which we used the Hexagonal Architecture, which we reviewed so that all of us share an ubiquitous language when talking about its components You know, driven ports and adapters, driving ports and adapters… or where they driver ports and adapters?.

Hexagonal Architecture

From there on, we started, as we should, by the basic principles, patterns and strategies that are common to software testing in any technology or programming language.

In the following weeks or months I’ll be blogging about these testing strategies we use at Agilogy. We may even write about Hexagonal Architecture before that, so we make it clear the way we talk about it.

In the meantime, I’m sharing the slides of the workshop we are working on:

You can download the PDF of the slides here.

Furthermore, I want to take the opportunity to review things I already shared in this blog, now that I have some names for the patterns and strategies I was talking about back then.

Pattern: Arrange, Act, Assert with State

In What is an automated test, again? I talked about how to test methods that have state:

// 1. Set initial state:
val ma = MemoryAdder()
ma.add(23)
// 2 and 3. Execute the method and assert
assertEquals(25, ma.add(2))
// 4. Collect the final state and assert
assertEquals(2, ma.lastInput)

I have identified this way of testing methods with state as a pattern I call Arrange, Act, Assert with State, or AAA with State for short.

Strategy: Integration Tests of Driven Adapters

In Testing and persistent state I showed how we can apply the AAA with State pattern to test components whose state is persisted:

// 1. Set initial state
val userId = 23L
val userName = "John"
conn.execute("delete from users")
conn.execute(
  "insert into users(id, name, last_visit), ?, ?, null", 
  userId, 
  userName
)
// 2 and 3. Execute the method and assert
val lastVisit = Instant.now()
val wasUpdated = updateUserLastVisit(userId, lastVisit)
assertTrue(wasUpdated)
// 4. Collect the final state and assert
val endState =
  conn.execute("select id,name,last_visit from users order by id"){ 
    (i, n, lv) -> User(id, n, lv)
  }
assertEquals(listOf(User(userId, userName, lastVisit)), endState)

If we apply this to an Hexagonal Architecture, we’ll see that the Driven Adapters are the perfect example of such a case. We say these are Integration Tests because we are testing (part of) our system in integration with an actual external system (an actual database, in the example).

Integration Tests of Driven Adapters

Design for testability

In Testing other side effects we started discussing how to test side effects other than state. That led us to discuss Dependency Injection and software design in general.

Testing the Agilogy Way - February 2, 2024 - Agilogy