Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 21 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,56 +209,46 @@ The NEO C# compiler supports the following options:
The NEO DevPack includes a comprehensive testing framework specifically designed for smart contracts. Here's how to create unit tests for your contracts:

```csharp
using Neo.SmartContract.Testing;
using Neo.SmartContract.Testing.TestingStandards;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.Testing.RuntimeCompilation;

namespace Example.SmartContract.MyContract.UnitTests
{
[TestClass]
public class MyContractTests : TestBase<ArtifactMyContract>
public class MyContractTests : ContractProjectTestBase
{
[TestInitialize]
public void TestSetup()
public MyContractTests()
: base("../Example.SmartContract.MyContract/Example.SmartContract.MyContract.csproj")
{
// Compile and generate testing artifacts
var (nef, manifest) = TestCleanup.EnsureArtifactsUpToDateInternal();
}

// Setup the test base with the compiled contract
TestBaseSetup(nef, manifest);
[TestInitialize]
public void SetUp()
{
EnsureContractDeployed();
}

[TestMethod]
public void TestMethod()
public void MyMethod_ReturnsExpectedValue()
{
// Access contract properties and methods through the Contract property
var result = Contract.MyMethod("parameter");
Assert.AreEqual("Expected result", result);

// Test storage changes after operations
var storedValue = Storage.Get(Contract.Hash, "myKey");
Assert.AreEqual("Expected storage value", storedValue);

// Verify emitted events
var notifications = Notifications;
Assert.AreEqual(1, notifications.Count);
Assert.AreEqual("ExpectedEvent", notifications[0].EventName);
}
}
}
```
```

#### Key Testing Features

1. **TestBase<T> Class**: Provides a base class for contract testing with access to the contract, storage, and notifications.
1. **ContractProjectTestBase**: Compiles the referenced contract project at test runtime and deploys it into an isolated `TestEngine` automatically.

2. **Automatic Artifact Generation**: The testing framework automatically compiles your contracts and generates testing artifacts.
2. **No Manual Artifacts**: The runtime compiler keeps NEF/manifest/debug assets in memory—no more checking generated files into source control.

3. **Direct Contract Interaction**: Access contract properties and methods directly through the strongly-typed `Contract` property.
3. **Direct Contract Interaction**: Interact with the contract through the dynamic `Contract` property, or project it onto helper interfaces for strongly-typed calls.

4. **Storage Simulation**: Test persistent storage operations without deploying to a blockchain.
4. **Storage Simulation**: Exercise persistent storage via the `Engine` and `Storage` helpers without deploying to a real network.

5. **Event Verification**: Validate that your contract emits the expected events.
5. **Event Verification**: Capture runtime notifications and logs to verify that the contract emits the expected events.

6. **Gas Consumption Analysis**: Track and analyze GAS costs of operations:

Expand All @@ -281,11 +271,11 @@ public void TestGasConsumption()

#### Setting Up the Test Project

1. Create a new test project for your contract
2. Add references to the Neo.SmartContract.Testing package and your contract project
3. Create a test class that inherits from TestBase<T>
4. Implement the TestSetup method to compile and initialize the contract
5. Write test methods for each contract feature or scenario
1. Create a new MSTest project for your contract tests.
2. Reference your contract project and add a project reference to `Neo.SmartContract.Testing.RuntimeCompilation`.
3. Create a test class that inherits from `ContractProjectTestBase`, supplying the path to the contract `.csproj` (and optional contract name).
4. Call `EnsureContractDeployed()` from `[TestInitialize]` to compile and deploy the contract before each test.
5. Write test methods that interact with the dynamic `Contract` property, asserting storage, notifications, and GAS as needed.

## Examples

Expand Down
12 changes: 8 additions & 4 deletions docs/security/access-control-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -853,17 +853,21 @@ public static bool IsAdminCached(UInt160 user)

```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.Testing;
using Neo.SmartContract.Testing.RuntimeCompilation;

[TestClass]
public class AccessControlTests : TestBase<RBACContract>
public class AccessControlTests : ContractProjectTestBase
{
public AccessControlTests()
: base("../path/to/RBACContract/RBACContract.csproj", contractName: "RBACContract")
{
}

[TestInitialize]
public void Setup()
{
// Setup test environment
var (nef, manifest) = TestCleanup.EnsureArtifactsUpToDateInternal();
TestBaseSetup(nef, manifest);
EnsureContractDeployed();
}

[TestMethod]
Expand Down
12 changes: 8 additions & 4 deletions docs/security/runtime-and-randomness.md
Original file line number Diff line number Diff line change
Expand Up @@ -702,16 +702,20 @@ public class BlockDataSecurity : SmartContract

```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.Testing;
using Neo.SmartContract.Testing.RuntimeCompilation;

[TestClass]
public class RandomnessSecurityTests : TestBase<RandomnessContract>
public class RandomnessSecurityTests : ContractProjectTestBase
{
public RandomnessSecurityTests()
: base("../path/to/RandomnessContract/RandomnessContract.csproj", contractName: "RandomnessContract")
{
}

[TestInitialize]
public void Setup()
{
var (nef, manifest) = TestCleanup.EnsureArtifactsUpToDateInternal();
TestBaseSetup(nef, manifest);
EnsureContractDeployed();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be moved to base virtual method in ContractProjectTestBase?

}

[TestMethod]
Expand Down
12 changes: 8 additions & 4 deletions docs/security/safe-arithmetic.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,16 +686,20 @@ public class FixedPointArithmetic : SmartContract

```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.SmartContract.Testing;
using Neo.SmartContract.Testing.RuntimeCompilation;

[TestClass]
public class ArithmeticSecurityTests : TestBase<SafeArithmeticContract>
public class ArithmeticSecurityTests : ContractProjectTestBase
{
public ArithmeticSecurityTests()
: base("../path/to/SafeArithmeticContract/SafeArithmeticContract.csproj", contractName: "SafeArithmeticContract")
{
}

[TestInitialize]
public void Setup()
{
var (nef, manifest) = TestCleanup.EnsureArtifactsUpToDateInternal();
TestBaseSetup(nef, manifest);
EnsureContractDeployed();
}

[TestMethod]
Expand Down
59 changes: 36 additions & 23 deletions docs/security/security-best-practices.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,54 +474,67 @@ public class GasEfficientSecurity : SmartContract
### Security Testing Framework

```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract.Testing.RuntimeCompilation;

[TestClass]
public class SecurityTests : TestBase<MyContract>
public class SecurityTests : ContractProjectTestBase
{
private static readonly Signer AdminSigner = TestEngine.GetNewSigner();
private static readonly Signer TestUser = TestEngine.GetNewSigner();

public SecurityTests()
: base("../path/to/MyContract/MyContract.csproj", contractName: "MyContract")
{
}

[TestInitialize]
public void Setup()
{
EnsureContractDeployed();
Engine.SetTransactionSigners(TestUser);
}

[TestMethod]
public void TestAccessControl()
{
// Test unauthorized access
Assert.ThrowsException<Exception>(() =>
{
Contract.AdminFunction();
});

// Test with proper authorization
Engine.SetCallingScriptHash(AdminAddress);
Assert.ThrowsException<Exception>(() => Contract.AdminFunction());

Engine.SetTransactionSigners(AdminSigner);
Assert.IsTrue(Contract.AdminFunction());
}

[TestMethod]
public void TestInputValidation()
{
// Test null inputs
Assert.IsFalse(Contract.ProcessData(null, "data"));

// Test oversized inputs
string largeData = new string('x', 10000);
Assert.IsFalse(Contract.ProcessData(ValidUser, largeData));

// Test valid inputs
Assert.IsTrue(Contract.ProcessData(ValidUser, "valid data"));

string largeData = new string('x', 10_000);
Assert.IsFalse(Contract.ProcessData(TestUser.Account, largeData));

Assert.IsTrue(Contract.ProcessData(TestUser.Account, "valid data"));
}

[TestMethod]
public void TestReentrancyProtection()
{
// Simulate reentrancy attack
bool firstCall = true;
Contract.OnExternalCall += () =>
{
if (firstCall)
{
firstCall = false;
Assert.ThrowsException<Exception>(() => Contract.WithdrawFunds(TestUser, 100));
Assert.ThrowsException<Exception>(() => Contract.WithdrawFunds(TestUser.Account, 100));
}
};
Assert.IsTrue(Contract.WithdrawFunds(TestUser, 100));

Assert.IsTrue(Contract.WithdrawFunds(TestUser.Account, 100));
}
}
```

This comprehensive security guide
```

This comprehensive security guide provides the foundation for developing secure Neo smart contracts. Remember that security is an ongoing process, and you should regularly review and update your security practices as the ecosystem evolves.
Loading
Loading