What I have so far
I have a Validator contract that is supposed to insert a key-value pair in the database contingently on a particular invariant being satisfied.
contract Validator
{
DataBase dataBase;
Aggregator aggregator;
function Validator(
DataBase _database,
Aggregator _aggregator
)
{
database = _database;
aggregator = _aggregator;
}
function insert(uint key, uint value)
{
dataBase.insert(key, value);
assert(aggregator.ComplexFunction() > 0);
}
}
I have a contract Aggregator (with various internal functions used as subroutines) which computes a complex function that necessitates informations from the DataBase and from an ExtraInfoProvider (which is a preexisting contract that I did not write).
contract Aggregator
{
DataBase database;
ExtraInfoProvider extraInfoProvider;
function Aggregator(DataBase _database, ExtraInfoProvider _extrainfoProvider)
{
database = _database;
extraInfoProvider = _extraInfoProvider;
}
function ComplexFunction()
{
// Compute complex function which involves iterating over
// all of the key-value pair in the database and consulting
// the ExtraInfoProvider
}
}
Finally I have a DataBase which supports iterating over the keys: note the use of getKeyCount and getKey to get around the impossibility of returning dynamically sized array in Solidity; (experienced solidity programmers are probably very familiar with this)
contract DataBase
{
function getKeyCount() returns (uint keyCount);
function getKey(uint index) returns (uint key);
function get(uint key) returns value;
function insert(key, value)
}
The problem with this organization is the complex nesting between the classes, which is reflected by the impossibility of creating clean unit-tests.
I would like to be able to create a MockDataBase and a MockAggregator in order to be able to cleanly UnitTest my Validator class without having to pass an instance of MockDataBase to the MockAggregator
What I would like to have
In an ideal world where Solidity supports passing dynamic arrays AND structures between contracts you could do something as follow:
struct KeyValuePair
{
uint key;
uint value;
}
contract Validator
{
DataBase dataBase;
Aggregator aggregator;
function Validator(
DataBase _database,
Aggregator _aggregator
)
{
database = _database;
aggregator = _aggregator;
}
function insert(uint key, uint value)
{
dataBase.insert(key, value);
KeyValuePair[] pairs = database.getAllKeyValuePairs();
assert(aggregator.ComplexFunction(pairs) > 0);
}
}
This would allow me to cleanly mock the Aggregator and the Database.
How could I achieve this result given the constraint present in Solidity?
(I could collect keys and values and create 2 dynamic arrays in the Validator and pass them to the Aggregator, but I do not like the idea of passing two arrays which are supposed to by in sync).
This is not entirely about testing
I don't intend to start an argument between Classicists and UnitTester: I perform both UnitTest and organic tests with real objects on my contracts. This is not entirely about testing: I am concerned about the pattern above as a general code smell.
I don't like the necessity for synchronization between the Aggregator and the Database: for example, if I want to augment my system to allow contracts upgrades, it would be way easier to substitute my Aggregator and my DataBase independently if they did not have mutual dependencies.