-
Notifications
You must be signed in to change notification settings - Fork 572
Description
Description
To improve the code quality and allow a deeper understanding of what happening under the hood of cosmos-sdk powered applications, we could add a unit test framework to the starport scaffolding
Implementation Details
We should follow best practices when defining unit test frameworks, this usually means table-driven testing and using test suites.
A sample test suite for the keeper package could look like:
The keeper test suite will contain a context and other structures needed to run the keeper correctly
// Keeper test suit enables the keeper package to be tested
type KeeperTestSuite struct {
suite.Suite
ctx sdk.Context
keeper Keeper
queryClient types.QueryClient
msgServer types.MsgServer
}The suite will be initialized by calling the SetupSuite() function which could look as follows: NOTE: we could initialise simapp here if we need some of the default keepers
// SetupTest creates a test suite to test the identifier
func (suite *KeeperTestSuite) SetupTest() {
keyIdentifier := sdk.NewKVStoreKey(types.StoreKey)
memKeyIdentifier := sdk.NewKVStoreKey(types.MemStoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyIdentifier, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(memKeyIdentifier, sdk.StoreTypeIAVL, db)
_ = ms.LoadLatestVersion()
ctx := sdk.NewContext(ms, tmproto.Header{ChainID: "foochainid"}, true, nil)
interfaceRegistry := ct.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
k := NewKeeper(
marshaler,
keyIdentifier,
memKeyIdentifier,
)
queryHelper := baseapp.NewQueryServerTestHelper(ctx, interfaceRegistry)
types.RegisterQueryServer(queryHelper, k)
queryClient := types.NewQueryClient(queryHelper)
msgServer := NewMsgServerImpl(*k) <== this doesn't really work atm, sorry for adding it here
suite.ctx, suite.keeper, suite.queryClient, suite.msgServer = ctx, *k, queryClient, msgServer
}Finally to run the full test suite we would call the RunKeeperTestSuite functions which would look as follows:
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}Then adding tests to the keeper package would look as follows
func (suite *KeeperTestSuite) TestGenericKeeperSetAndGet() {
testCases := []struct {
msg string
identifier types.DidDocument
expPass bool
}{
{
"data stored successfully",
types.DidDocument{
"context",
"did:cash:1111",
nil,
nil,
},
true,
},
}
for _, tc := range testCases {
suite.keeper.Set(suite.ctx,
[]byte(tc.identifier.Id),
[]byte{0x01},
tc.identifier,
suite.keeper.MarshalIdentifier,
)
suite.keeper.Set(suite.ctx,
[]byte(tc.identifier.Id+"1"),
[]byte{0x01},
tc.identifier,
suite.keeper.MarshalIdentifier,
)
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
if tc.expPass {
_, found := suite.keeper.Get(
suite.ctx,
[]byte(tc.identifier.Id),
[]byte{0x01},
suite.keeper.UnmarshalIdentifier,
)
suite.Require().True(found)
allEntities := suite.keeper.GetAll(
suite.ctx,
[]byte{0x01},
suite.keeper.UnmarshalIdentifier,
)
suite.Require().Equal(2, len(allEntities))
} else {
// TODO write failure cases
suite.Require().False(tc.expPass)
}
})
}
}😄