I think you missed something here. In the context of a Blockchain environment, you must consider a method/function which writes data and a method which reads data in a completely different manner.
Read data (call):
That's really easy to read data on the Blockchain, you don't have to go too far, your node already contains the whole blockchain. It's kind of accessing the local storage.
In the Ethereum ecosystem, You will have to write a read-only function that doesn't modify the state (variables).
For example
Solidity
function getCampaignDetails(uint campaignID) constant returns (uint256, uint256) {
Campaign c = campaigns[campaignID];
return (c.fundingGoal, c.amount);
}
Web3
CrowdFunding.deployed().then(function(instance) {
return instance.getDetails.call(id);
}).then(function(details) {
console.log(details);
})
I use the keyword constant to enforce the compiler that's a read-only function (optional). And a Web3 call returns a promise that contains the result of the function (see doc).
A call doesn't cost any gas, can't receive ether, is synchronous and the return value of the contract function is returned immediately.
Write data (transaction):
To write data on the blockchain, you have to send a transaction, it's way more complicated because the transaction has to be broadcast on the network, mined, validated and published.
For example
Solidity
function newCampaign(address beneficiary, uint goal) {
campaignID = numCampaigns++;
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}
Web3
CrowdFunding.deployed().then(function(instance) {
return instance.newCampaign(account_two, 2);
}).then(function (transactionHash) {
console.log(transactionHash); // useful for tracking
})
In this example, the function newCampaign changes the state by adding a new campaign to the map.
A transaction is asynchronous, the success of execution depends on the miner so you can't expect any data in return.
However, you can send an event to share some data.
So basically, your code can't work because you need this campaignID. I would store the campaign creator and the campaignID in the Campaign struct, build a call function that returns my campaigns (Filter using campaignCreator == msg.sender) and then select a campaign and run your test.
I'd like to make a suggestion.
In the TestMetaCoin.sol
example, you can see that the contract owner address is used. (See tx.origin
) Therefore, I see no reason why you couldn't hard-code other account addresses in a Solidity library file and then import the library into your test. You might be then be able to use $ testrpc --account="<privatekey>,balance" [--account="<privatekey>,balance"]
to ensure that the addresses in your library are re-used each time you launch.
You can do some fairly complicated things with Solidity tests. (See http://truffleframework.com/tutorials/testing-for-throws-in-solidity-tests) However, judging by the truffle docs, I get the impression that they may not be designed for your use case.
http://truffleframework.com/docs/getting_started/solidity-tests
Best Answer
Assuming your successfully deployed the contract with
$ truffle migrate --reset
... Need to reset every time you start testrpc.I think the problem is it's asynchronous and you're not waiting for
Congress.deployed();
to finish.I'm not up to full speed in 3.x due to the breaking changes, but based on what I've read in http://truffleframework.com/tutorials/upgrading-from-truffle-2-to-3#contract-abstractions-deployed-is-now-thennable, deployed is now thennable, so something like ...
Hope it helps.