JUnit 5 - Running Test Classes in Order to Preserve System State

Viewed 45

I'm designing a platform which could be described as a trading platform for buying and selling collectibles, such as trading cards, stamps, coins, etc.

There are several processes which change the state of assets being traded through the platform; for example:

  • Advertising an asset for sale.

  • Receiving purchase offers for the asset.

  • Accepting a purchase offer.

  • Receiving payment.

For each of these processes, there is a unit of logic which performs each action, and are unit tested using JUnit 5; for example, testing that an asset can be advertised looks like this:

class AdvertiseAssetTests : TestBase() {

    private lateinit var asset: Asset
    
    override fun initialize() {
        asset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
    }
    
    @Test
    fun `asset should be adverised with the correct status`() {
        assertEquals(AssetStatus.ADVERTISED, asset.status)
    }
}

In the above class, the final state of the system under test represents advertisement of a single asset against which assertions are made.

The next test would be to assert that offers can be created for the asset; for example:

class CreateAssetOfferTests : TestBase() {

    private lateinit var asset: Asset
    private lateinit var offer: Offer
    
    override fun initialize() {
        asset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
        offer = systemUnderTest.offer(asset, EXAMPLE_TRADING_CARD_OFFER)
    }
    
    @Test
    fun `offer should be created for the specified asset`() {
        assertEquals(asset.id, offer.assetId)
    }
    
    @Test
    fun `offer should be created with the correct status`() {
        assertEquals(OfferStatus.OFFERED, offer.status)
    }
}

...and so on until the final test, which would be to test that the payment for an asset has been received; for example:

class ReceiveAssetPaymentTests : TestBase() {

    private lateinit var asset: Asset
    private lateinit var offer: Offer
    
    override fun initialize() {
        val advertisedAsset = systemUnderTest.advertise(EXAMPLE_TRADING_CARD)
        val initialOffer = systemUnderTest.offer(advertisedAsset, EXAMPLE_TRADING_CARD_OFFER)
        offer = systemUnderTest.acceptOffer(initialOffer)
        asset = systemUnderTest.receivePayment(advertisedAsset)        
    }
    
    @Test
    fun `asset status should be payment received`() {
        assertEquals(AssetStatus.PAYMENT_RECEIVED, asset.id)
    }
}

As you can see, the further down the process that needs testing, the more test code needs to be added; for example, testing that a payment has been received requires the test setup to advertise, offer, accept an offer and receive payment before any assertions can be made.

This seems like a lot of duplication, since the former processes have already been tested. The problem is that the setup of each test takes longer and longer; for example:

  • Asset advertisement: 30sec
  • Offer creation: 30sec (advertisement) + 25sec (offer)
  • Offer acceptance: 30sec (advertisement) + 25sec (offer) + 25sec (acceptance)
  • Payment received: 30sec (advertisement) + 25sec (offer) + 25sec (acceptance) + 10sec (payment received)

Is there a way to organise the tests such that the state of the system can allow test classes to execute in order; for example, run AdvertiseAssetTests and then run CreateAssetOfferTests and finally run ReceiveAssetPaymentTests and maintain the state of the system after each test class run?

Is this possible?

0 Answers
Related