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?