plutus icon indicating copy to clipboard operation
plutus copied to clipboard

Unit tests for validators

Open volodyad opened this issue 3 years ago • 3 comments

At the moment tests are written via Traces module. it is looks more like integration tests approach, we run wallet code, which starts validators, and after that we test system state changes.

Issue is that we can not test validators in isolation. If my wallet code do some validation that duplicate validator checks - there is no way to ensure that validator checks are actually works as expected. (e.g if we expect price parameter to be greater than 0 in a wallet and in a validator, our unit test will cover only wallet part, there is no way to test validator check)

Ideally we should be able to test validator in isolation. Maybe api should provide some api to mock and test validator code.

volodyad avatar Jun 14 '21 09:06 volodyad

On-and off-chain code can use the same functions, and you can unit-test those with any Haskell unit-testing framework like hunit.

Could you give a clearer example of a function that you would find it hard to test?

michaelpj avatar Jun 15 '21 11:06 michaelpj

I have wallet endpoint to set item on sell, it has price validation

sell :: 
    HasBlockchainActions s 
    -> SellParams 
    -> Contract w s Text Text
sell market SellParams{..} = do
 when (spSellPrice <= 0) $ throwError "sell price should be greater than zero"

I have the same validation in the validator

traceIfFalse "price should be grater 0" (sellPrice outDatum > 0)

With provided test framework, I am not able to test Validator, when price value is 0, it will fail in the Contract, and can be asserted with assertContractError. But I can not assert that with assertFailedTransaction, as it always fail in a Contract monad. We need to ensure that Validator works as expected in isolation from PAB.

Would be good to have some samples of hunit in the uses cases, which covers Validator tests in isolation.

volodyad avatar Jun 16 '21 08:06 volodyad

Basically you will have to build a ScriptContext object and then and pass it to the validator. On the most basic level you will have to set all the inputs, outputs, signatures and forge, other parameters are mostly ignored. and then call your validator with that ScriptContext object.

I've written a simple implementation for it. The main function that executes the validator has following signature

executeContext :: (Data->Data->ScriptContext ->Bool)->TestContextBuilder-> POSIXTimeRange ->Value ->Bool
executeContext validatorFunction (TestContextBuilder cInputs cOutputs signatures) timeRange forge= ...

Relevant files:

I hope something like this will be included in the plutus test library

mesudip avatar Aug 03 '21 08:08 mesudip

@mesudip thank you for jumping in and providing a workaround.

All the testing machinery has moved to the plutus-apps repo developed by the Plutus Tools team. I asked them a few days ago if they wanted to provide the functionality requested in this ticket and they didn't.

The requested functionality does sound useful to me, but I don't see any option regarding how to move this ticket forward, so I'm going to close it as "won't do" unfortunately.

effectfully avatar Apr 12 '23 14:04 effectfully