Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement improved testing structure #3

Open
whoabuddy opened this issue Sep 30, 2022 · 1 comment
Open

Implement improved testing structure #3

whoabuddy opened this issue Sep 30, 2022 · 1 comment

Comments

@whoabuddy
Copy link
Contributor

Original Contract Tests

The current testing structure in citycoins/contracts contains a large amount of redundant code, and as the project goes each set of tests is becoming more complex.

While unit testing can be powerful, it might be worth taking a look at other approaches, especially given this new repo is using a vanilla Clarinet setup instead of the custom one used before.

Some related issues and backstory:

Current Protocol Tests

Starting with the contracts for CCIP-012 Phase 2, the current protocol testing structure is similar to the original one with some basic improvements.

  • dspec library is removed and tests are run in default Clarinet formatting
  • utils folder contains general items used by all tests
    • a single reference for dependencies (easier to update across all tests)
    • contains references to common contract names, organized by category
      e.g. EXTENSIONS.CCD001_DIRECT_EXECUTE
  • contracts are organized by categories related to the DAO structure
    • extensions: once enabled, can be used until disabled
    • proposals: changes that are executed one time
    • traits: trait definitions for DAO operations
  • models exist for each type of extension or contract, and are extended for naming (or other customization) within the same file
    • also organized by extensions, proposals, traits
    • includes the error codes for the contract for easy access/readability
      e.g. BaseDao.ErrCode.ERR_INVALID_EXTENSION
    • includes the public and read only contract functions used by tests
      e.g. baseDao.construct(sender, PROPOSALS.CCIP_012)
  • unit tests are run against each contract, verifying the error and success paths of each function
    • also organized by extensions, proposals, traits

Ideal Scenario

Given the CityCoins protocol functions are going to be managed by single contracts instead of one per city, this could be a chance to not only simplify but revise the testing structure to take advantage of more advanced techniques such as model-based testing or fuzzing.

Goals

  • abstract, simplify, and reduce redundant code
  • expand amount of properties tested for
  • find better ways to handle complex scenarios
@moodmosaic
Copy link

abstract, simplify, and reduce
expand amount of properties tested for

Adding property-based tests can help. The existing (example-based) tests often serve as API documentation, and property-based tests are there to detect the unexpected.

Both types of tests can be useful, but they may also look different at the same time:

NOTE: The property-test in the link above may be rewritten as:

Clarinet.fuzz({
  name: "succeeds and store values when called once or more per block over one or more blocks",
  runs: 10,
  async fn(chain: Chain, sender: Account, values: number[], numTxs: number) {
    // arrange
    const setValuesTx = example.setValues(values, sender);

    // act
    const receipt = chain.mineBlock(
      [...Array(numTxs).keys()].map(() => setValuesTx)).receipts[0];

    // assert
    receipt.result.expectOk().expectBool(true);

    let blockHeight = chain.blockHeight - 1;
    for (let value of values) {
      const existing = valuesMap.has(blockHeight) ? valuesMap.get(blockHeight) : 0;
      valuesMap.set(blockHeight, value * numTxs + existing);

      example
        .getValue(blockHeight)
        .expectSome()
        .expectUint(valuesMap.get(blockHeight));
      blockHeight++;
    }
  },
});

using the stuff in hirosystems/clarinet#398 (comment) once/if released.


find better ways to handle complex scenarios

One pattern for this is to define one or more commands for each one of the public functions of the contract.

image

Image taken and modified from Spotify's article on the same subject.


As an extra bonus, property-based testing (and model-based testing) can help in detecting the unexpected. HTH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants