Skip to content

Commit 54778e7

Browse files
committed
added unit testing examples
1 parent 991bb2e commit 54778e7

File tree

2 files changed

+145
-3
lines changed

2 files changed

+145
-3
lines changed

docs/quick-start/developers/_deploy_donation.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,21 @@ aelf-command create
2222
ℹ️ Note: If you do not save your account information (by selecting n or N), do not export the wallet password. Only **proceed to the next** step if you have saved your account information.
2323
:::
2424

25-
- Next, enter and confirm your password. Then export your wallet password as shown below:
25+
- Next, enter and confirm your password. Then export your wallet address and password as shown below:
26+
27+
<Tabs>
28+
<TabItem value="Linux and macOs" label="Linux and macOs" default>
29+
```bash title="Terminal"
30+
export WALLET_ADDRESS="YOUR_WALLET_ADDRESS"
31+
```
32+
</TabItem>
33+
34+
<TabItem value="Windows" label="Windows">
35+
```bash title="Command Prompt"
36+
$env:WALLET_ADDRESS = "YOUR_WALLET_ADDRESS"
37+
```
38+
</TabItem>
39+
</Tabs>
2640

2741
<Tabs>
2842
<TabItem value="Linux and macOs" label="Linux and macOs" default>

docs/quick-start/developers/donation-dapp/index.md

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: Moderately complex smart contract
66

77
**Description**: The Donation dApp contract is moderately complex, enabling functionalities such as creating, editing, and deleting donation campaigns, tracking donations, and rewarding contributors. It also supports user-specific data retrieval and ensures secure interactions for managing ELF token-based campaigns.
88

9-
**Purpose**: To provide a practical understanding of donation management systems using smart contracts, focusing on features like campaign creation, user-specific interactions, secure fund management, and reward distribution to enhance blockchain-based philanthropy.
9+
**Purpose**: To provide a practical understanding of donation management systems using smart contracts, focusing on features like campaign creation, user-specific interactions, secure fund management, and reward distribution to enhance blockchain-based philanthropy. This tutorial also emphasizes unit testing practices for smart contracts to ensure reliability and security.
1010

1111
**Difficulty Level**: Moderate
1212

@@ -689,7 +689,135 @@ namespace AElf.Contracts.DonationApp
689689
dotnet build
690690
```
691691

692-
You should see **DonationContract.dll.patched** in the directory `donation/src/bin/Debug/net6.0`
692+
You should see **DonationApp.dll.patched** in the directory `donation/src/bin/Debug/net6.0`
693+
694+
### Unit Testing Smart Contract
695+
696+
Unit testing is crucial for ensuring the reliability and security of smart contracts. Let's look at some test cases for the critical `Donate` method:
697+
698+
```csharp title="DonationDAppTests.cs"
699+
[Fact]
700+
public async Task Donate_Success()
701+
{
702+
// Arrange
703+
await DonationContract.Initialize.SendAsync(new InitializeInput());
704+
705+
var createResult = await DonationContract.CreateCampaign.SendAsync(new CreateCampaignInput
706+
{
707+
Title = "Test Campaign",
708+
Description = "Test Description",
709+
TargetAmount = 100_00000000,
710+
StartTime = GetTimestamp(),
711+
EndTime = GetTimestamp(30)
712+
});
713+
var campaignId = createResult.Output.Value;
714+
715+
// Approve token spending
716+
await ApproveTokenAsync(DonationContractAddress, DefaultDonationAmount);
717+
718+
// Act
719+
var result = await DonationContract.Donate.SendAsync(new DonateInput
720+
{
721+
CampaignId = campaignId,
722+
Amount = DefaultDonationAmount
723+
});
724+
725+
// Assert
726+
result.TransactionResult.Status.ShouldBe(TransactionResultStatus.Mined);
727+
728+
var campaign = await DonationContract.GetCampaign.CallAsync(new StringValue { Value = campaignId });
729+
campaign.CurrentAmount.ShouldBe(DefaultDonationAmount);
730+
731+
var donatorList = await DonationContract.GetDonatorList.CallAsync(new StringValue { Value = campaignId });
732+
donatorList.Value.Count.ShouldBe(1);
733+
donatorList.Value[0].Donor.ShouldBe(DefaultAddress);
734+
donatorList.Value[0].Amount.ShouldBe(DefaultDonationAmount);
735+
}
736+
737+
[Fact]
738+
public async Task Donate_CampaignEnded_ShouldFail()
739+
{
740+
// Arrange
741+
await DonationContract.Initialize.SendAsync(new InitializeInput());
742+
743+
var createResult = await DonationContract.CreateCampaign.SendAsync(new CreateCampaignInput
744+
{
745+
Title = "Test Campaign",
746+
Description = "Test Description",
747+
TargetAmount = 100_00000000,
748+
StartTime = GetTimestamp(-30), // Started 30 days ago
749+
EndTime = GetTimestamp(-1) // Ended yesterday
750+
});
751+
var campaignId = createResult.Output.Value;
752+
753+
await ApproveTokenAsync(DonationContractAddress, DefaultDonationAmount);
754+
755+
// Act & Assert
756+
var exception = await Assert.ThrowsAsync<Exception>(() =>
757+
DonationContract.Donate.SendAsync(new DonateInput
758+
{
759+
CampaignId = campaignId,
760+
Amount = DefaultDonationAmount
761+
}));
762+
exception.Message.ShouldContain("Campaign has ended");
763+
}
764+
765+
[Fact]
766+
public async Task Donate_ExceedTargetAmount_ShouldFail()
767+
{
768+
// Arrange
769+
await DonationContract.Initialize.SendAsync(new InitializeInput());
770+
771+
var targetAmount = 10_00000000; // 10 ELF
772+
var createResult = await DonationContract.CreateCampaign.SendAsync(new CreateCampaignInput
773+
{
774+
Title = "Test Campaign",
775+
Description = "Test Description",
776+
TargetAmount = targetAmount,
777+
StartTime = GetTimestamp(),
778+
EndTime = GetTimestamp(30)
779+
});
780+
var campaignId = createResult.Output.Value;
781+
782+
// First donation equals target amount
783+
await ApproveTokenAsync(DonationContractAddress, targetAmount);
784+
await DonationContract.Donate.SendAsync(new DonateInput
785+
{
786+
CampaignId = campaignId,
787+
Amount = targetAmount
788+
});
789+
790+
// Try to donate more
791+
await ApproveTokenAsync(DonationContractAddress, DefaultDonationAmount);
792+
793+
// Act & Assert
794+
var exception = await Assert.ThrowsAsync<Exception>(() =>
795+
DonationContract.Donate.SendAsync(new DonateInput
796+
{
797+
CampaignId = campaignId,
798+
Amount = DefaultDonationAmount
799+
}));
800+
exception.Message.ShouldContain("Campaign target amount reached");
801+
}
802+
```
803+
804+
These test cases demonstrate:
805+
806+
1. **Successful Donation**: Tests a complete donation flow, including:
807+
- Campaign creation
808+
- Token approval
809+
- Donation execution
810+
- Verification of campaign amount and donor list
811+
812+
2. **Campaign End Date Validation**: Tests that donations cannot be made to expired campaigns
813+
814+
3. **Target Amount Validation**: Tests that donations cannot exceed the campaign's target amount
815+
816+
Each test follows the Arrange-Act-Assert pattern and uses the `Shouldly` assertion library for clear, readable assertions.
817+
818+
:::Note
819+
For a deeper dive into unit testing smart contracts, check out the complete test suite in our [GitHub repository](https://github.com/AElfProject/aelf-samples/tree/master/donation/1-smart-contract/test).
820+
:::
693821

694822
## Step 3 - Deploy Smart Contract
695823

0 commit comments

Comments
 (0)