From 6539626fa3d35e4d1e08c7e3ee28c1b707c5276f Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Fri, 10 Apr 2020 23:48:17 +0530 Subject: [PATCH 1/4] testing examples added --- .../a-unit-testing-custom-compiler-config.png | Bin 0 -> 56455 bytes docs/unittesting.md | 41 ++- docs/unittesting_examples.md | 318 +++++++++++++++++- 3 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 docs/images/a-unit-testing-custom-compiler-config.png diff --git a/docs/images/a-unit-testing-custom-compiler-config.png b/docs/images/a-unit-testing-custom-compiler-config.png new file mode 100644 index 0000000000000000000000000000000000000000..8e3719c2848d264f08c6917d32318b62a8ea4900 GIT binary patch literal 56455 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 literal 0 HcmV?d00001 diff --git a/docs/unittesting.md b/docs/unittesting.md index 431d148a54..0e998301cb 100644 --- a/docs/unittesting.md +++ b/docs/unittesting.md @@ -30,7 +30,7 @@ Apart from this, Remix allows usage of some special functions to make testing mo * `afterEach()` - Runs after each test * `afterAll()` - Runs after all tests -To get started, see [this](https://github.com/ethereum/remix/blob/master/remix-tests/tests/examples_4/SafeMath_test.sol) for sample implementation. +To get started, see [this simple example](./unittesting_examples.html#simple-example). Run Tests ------------------ @@ -39,6 +39,45 @@ Click the button "Run tests" to executes all tests whose box has been checked be ![](images/a-unit-testing-run-result.png) +Customization +------------------ +Remix facilitates users with various types of customizations to test a contract properly. + +**1. Custom Compiler Context** + +`Solidity Unit Testing` refers `Solidity Compiler` plugin for compiler configurations. One can provide customized inputs for `Compiler`, `EVM Version` & `Enable Optimization`. + +![](images/a-unit-testing-custom-compiler-config.png) + +**2. Custom Transaction Context** + +For a contract method interaction, prime parameters of transaction are `from` address, `value` & `gas`. Usually, we need to test method's behaviour under different values of these parameters. + +Remix provides the functionality of custom `msg.sender` & `msg.value` of transaction using method devdoc like: + +``` +/// #sender: account-0 +/// #value: 10 +function checkSenderIs0AndValueis10 () public payable{ + Assert.equal(msg.sender, TestsAccounts.getAccount(0), "wrong sender in checkSenderIs0AndValueis10"); + Assert.equal(msg.value, 10, "wrong value in checkSenderIs0AndValueis10"); +} +``` + +**Note:** To use custom `msg.sender` functionality, `remix_accounts.sol` should be imported in your test file. + +Complete example can be seen in [examples](./unittesting_examples) section. + +Regarding `gas`, Remix estimate the required gas for each transaction internally. Still if a contract deployment fails with `Out-of-Gas` error, it tries to redeploy it by doubling the gas. Deployment failing with double gas will show error: `contract deployment failed after trying twice: The contract code couldn\'t be stored, please check your gas limit` + + +Points to remember +------------------ + +* A test contract can not have a method with parameters. Having one such method will show error: `Method 'methodname' can not have parameters inside a test contract` +* No. of test accounts are `3` before remix-ide release v0.10.0 and `10` afterwards +* A test file which imports `remix_accounts.sol` might not compile successfully with `Solidity Compiler` plugin but it will work fine with Solidity Unit Testing plugin + Continuous integration ---------------------- diff --git a/docs/unittesting_examples.md b/docs/unittesting_examples.md index 964533cc9d..264ca61d7a 100644 --- a/docs/unittesting_examples.md +++ b/docs/unittesting_examples.md @@ -1,4 +1,318 @@ -Examples +Testing by Example ============ -coming soon \ No newline at end of file +Here are some examples which can give you better understanding to plan your tests. + +**Note:** Examples in this section are intended to give you a push for development. We don't recommend to rely on them without verifying at your end. + +### 1. Simple example +In this example, we test setting & getting variables. + +Contract/Program to be tested: `Simple_storage.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; + +contract SimpleStorage { + uint public storedData; + + constructor() public { + storedData = 100; + } + + function set(uint x) public { + storedData = x; + } + + function get() public view returns (uint retVal) { + return storedData; + } +} +``` +Test contract/program: `simple_storage_test.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +import "remix_tests.sol"; +import "./Simple_storage.sol"; + +contract MyTest { + SimpleStorage foo; + + // beforeEach works before running each test + function beforeEach() public { + foo = new SimpleStorage(); + } + + /// Test if initial value is set correctly + function initialValueShouldBe100() public returns (bool) { + return Assert.equal(foo.get(), 100, "initial value is not correct"); + } + + /// Test if value is set as expected + function valueIsSet200() public returns (bool) { + foo.set(200); + return Assert.equal(foo.get(), 200, "value is not 200"); + } +} +``` + +### 2. Testing a method involving `msg.sender` +In Solidity, `msg.sender` plays a great role in access management of a smart contract methods interaction. Different `msg.sender` can help to test a contract involving multiple accounts with different roles. Here is an example for testing such case: + +Contract/Program to be tested: `Sender.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +contract Sender { + address private owner; + + constructor() public { + owner = msg.sender; + } + + function updateOwner(address newOwner) public { + require(msg.sender == owner, "only current owner can update owner"); + owner = newOwner; + } + + function getOwner() public view returns (address) { + return owner; + } +} +``` + +Test contract/program: `Sender_test.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +import "remix_tests.sol"; // this import is automatically injected by Remix +import "remix_accounts.sol"; +import "./Sender.sol"; +// Inherit 'Sender' contract +contract SenderTest is Sender { + address account0; + address account1; + address account2; + + /// Initiate accounts variable + function beforeAll() public { + account0 = TestsAccounts.getAccount(0); + account1 = TestsAccounts.getAccount(1); + account2 = TestsAccounts.getAccount(2); + } + + /// Test if initial owner is set correctly + function testInitialOwner() public { + // account-0 is default account, so current owner should be account0 + Assert.equal(getOwner(), account0, 'owner should be account-0'); + } + + /// Update owner. This method will be called by account0 as there is not custom sender appointed + function updateOwnerOnce() public { + // check method call is as expected + Assert.ok(msg.sender == account0, 'caller should default account i.e. account0'); + // update owner address to account1 + updateOwner(account1); + // check if owner is set to expected account + Assert.equal(getOwner(), account1, 'owner should be updated to account1'); + } + + /// Update owner again by defining custom sender + /// #sender: account-1 + function updateOwnerOnceAgain() public { + // check if caller is custom and is as expected + Assert.ok(msg.sender == account1, 'caller should be custom account i.e. account1'); + // update owner address to account2. This will be successful because account1 is current owner & caller both + updateOwner(account2); + // check if owner is set to expected account i.e. account2 + Assert.equal(getOwner(), account2, 'owner should be updated to account2'); + } +} +``` + +### 3. Testing method execution + +With Solidity, one can verify the changes made by a method in storage by accessing those variables out of a contract. But while testing whether method execution was successful and if execution failed, what was the reason behind it, can also be an importnat case. +Solidity introduced `try-catch` statement in version 0.6.0 which helps a lot to solve this purpose. Previously, this could be achieved using low-level call to some extent. Here is the example to test such a scenario: + +Contract/Program to be tested: `AttendanceRegister.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +contract AttendanceRegister { +struct Student{ + string name; + uint class; + } +event Added(string name, uint class, uint time); +mapping(uint => Student) public register; // roll number => student details + function add(uint rollNumber, string memory name, uint class) public returns (uint256){ + require(class > 0 && class <= 12, "Invalid class"); + require(register[rollNumber].class == 0, "Roll number not available"); + Student memory s = Student(name, class); + register[rollNumber] = s; + emit Added(name, class, now); + return rollNumber; + } + + function getStudentName(uint rollNumber) public view returns (string memory) { + return register[rollNumber].name; + } +} +``` + +Test contract/program: `AttendanceRegister_test.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +import "remix_tests.sol"; // this import is automatically injected by Remix. +import "./AttendanceRegister.sol"; +contract AttendanceRegisterTest { + + AttendanceRegister ar; + + /// 'beforeAll' runs before all other tests + function beforeAll () public { + // Create an instance of contract to be tested + ar = new AttendanceRegister(); + } + + /// For solidity version greater or equal to 0.6.0, + /// See: https://solidity.readthedocs.io/en/v0.6.0/control-structures.html#try-catch + /// Test 'add' using try-catch + function testAddSuccessUsingTryCatch() public { + // This will pass + try ar.add(101, 'secondStudent', 11) returns (uint256 r) { + Assert.equal(r, 101, 'wrong rollNumber'); + } catch Error(string memory /*reason*/) { + // This is executed in case + // revert was called inside getData + // and a reason string was provided. + Assert.ok(false, 'failed with reason'); + } catch (bytes memory /*lowLevelData*/) { + // This is executed in case revert() was used + // or there was a failing assertion, division + // by zero, etc. inside getData. + Assert.ok(false, 'failed unexpected'); + } + } + + /// Test failure case of 'add' using try-catch + function testAddFailureUsingTryCatch1() public { + // This will revert on 'require(class > 0 && class <= 12, "Invalid class");' for class '13' + try ar.add(101, 'secondStudent', 13) returns (uint256 r) { + Assert.ok(false, 'method execution should fail'); + } catch Error(string memory reason) { + // Compare failure reason, check if it is as expected + Assert.equal(reason, 'Invalid class', 'failed with unexpected reason'); + } catch (bytes memory /*lowLevelData*/) { + Assert.ok(false, 'failed unexpected'); + } + } + + /// Test another failure case of 'add' using try-catch + function testAddFailureUsingTryCatch2() public { + // This will revert on 'require(register[rollNumber].class == 0, "Roll number not available");' for rollNumber '101' + try ar.add(101, 'secondStudent', 11) returns (uint256 r) { + Assert.ok(false, 'method execution should fail'); + } catch Error(string memory reason) { + // Compare failure reason, check if it is as expected + Assert.equal(reason, 'Roll number not available', 'failed with unexpected reason'); + } catch (bytes memory /*lowLevelData*/) { + Assert.ok(false, 'failed unexpected'); + } + } + + /// For solidity version less than 0.6.0, low level call can be used + /// See: https://solidity.readthedocs.io/en/v0.6.0/units-and-global-variables.html#members-of-address-types + /// Test success case of 'add' using low level call + function testAddSuccessUsingCall() public { + bytes memory methodSign = abi.encodeWithSignature('add(uint256,string,uint256)', 102, 'firstStudent', 10); + (bool success, bytes memory data) = address(ar).call(methodSign); + // 'success' stores the result in bool, this can be used to check whether method call was successful + Assert.equal(success, true, 'execution should be successful'); + // 'data' stores the returned data which can be decoded to get the actual result + uint rollNumber = abi.decode(data, (uint256)); + // check if result is as expected + Assert.equal(rollNumber, 102, 'wrong rollNumber'); + } + + /// Test failure case of 'add' using low level call + function testAddFailureUsingCall() public { + bytes memory methodSign = abi.encodeWithSignature('add(uint256,string,uint256)', 102, 'duplicate', 10); + (bool success, bytes memory data) = address(ar).call(methodSign); + // 'success' will be false if method execution is not successful + Assert.equal(success, false, 'execution should be successful'); + } +} +``` + + +### 4. Testing a method involving `msg.value` +In Solidity, ether can be passed along with a method call which is accessed inside contract as `msg.value`. Sometimes, multiple calculations in a method are performed based on `msg.value` which can be tested with various values using Remix's Custom transaction context. See the example: + +Contract/Program to be tested: `Value.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +contract Value { + uint256 public tokenBalance; + + constructor() public { + tokenBalance = 0; + } + + function addValue() payable public { + tokenBalance = tokenBalance + (msg.value/10); + } + + function getTokenBalance() view public returns (uint256) { + return tokenBalance; + } +} +``` +Test contract/program: `Value_test.sol` + +``` +pragma solidity >=0.4.22 <0.7.0; +import "remix_tests.sol"; +import "./Value.sol"; +contract ValueTest{ + Value v; + + function beforeAll() public { + // create a new instance of Value contract + v = new Value(); + } + + /// Test initial balance + function testInitialBalance() public { + // initially token balance should be 0 + Assert.equal(v.getTokenBalance(), 0, 'token balance should be 0 initially'); + } + + /// For Solidity version greater than 0.6.1 + /// Test 'addValue' execution by passing custom ether amount + /// #value: 200 + function addValueOnce() public payable { + // check if value is same as provided through devdoc + Assert.equal(msg.value, 200, 'value should be 200'); + // execute 'addValue' + v.addValue{gas: 40000, value: 200}(); // introduced in Solidity version 0.6.2 + // As per the calculation, check the total balance + Assert.equal(v.getTokenBalance(), 20, 'token balance should be 20'); + } + + /// For Solidity version less than 0.6.2 + /// Test 'addValue' execution by passing custom ether amount again using low level call + /// #value: 100 + function addValueAgain() public payable { + Assert.equal(msg.value, 100, 'value should be 100'); + bytes memory methodSign = abi.encodeWithSignature('addValue()'); + (bool success, bytes memory data) = address(v).call.gas(40000).value(100)(methodSign); + Assert.equal(success, true, 'execution should be successful'); + Assert.equal(v.getTokenBalance(), 30, 'token balance should be 30'); + } +} +``` \ No newline at end of file From 343cb0442c91dbc6f5c491e303d4f0096688c4ea Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Mon, 13 Apr 2020 13:26:28 +0530 Subject: [PATCH 2/4] added a section for remix-tests --- docs/unittesting.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/unittesting.md b/docs/unittesting.md index 0e998301cb..046ee4ad51 100644 --- a/docs/unittesting.md +++ b/docs/unittesting.md @@ -78,10 +78,11 @@ Points to remember * No. of test accounts are `3` before remix-ide release v0.10.0 and `10` afterwards * A test file which imports `remix_accounts.sol` might not compile successfully with `Solidity Compiler` plugin but it will work fine with Solidity Unit Testing plugin -Continuous integration +Remix-tests ---------------------- -remix-tests is also a CLI, it can be used in a continuous integration environment which support node.js. -Please find more information in the [remix-tests repository](https://github.com/ethereum/remix/tree/master/remix-tests) +`remix-tests` is the module which works underneath of remix-ide `Solidity Unit Testing` plugin. -See also: example [Su Squares contract](https://github.com/su-squares/ethereum-contract/tree/e542f37d4f8f6c7b07d90a6554424268384a4186) and [Travis build](https://travis-ci.org/su-squares/ethereum-contract/builds/446186067) that uses remix-tests for continuous integration testing. +`remix-tests` is an [NPM package](https://www.npmjs.com/package/remix-tests). It can also be used as a CLI/CI solution, supporting node.js. Find more information about this type of usage in the [remix-tests repository](https://github.com/ethereum/remix/tree/master/remix-tests#as-command-line-interface) + +For CI implementation example, see [Su Squares contract](https://github.com/su-squares/ethereum-contract/tree/e542f37d4f8f6c7b07d90a6554424268384a4186) and [Travis build](https://travis-ci.org/su-squares/ethereum-contract/builds/446186067) that uses `remix-tests` for continuous integration testing. From ae1e7825b7e095ccf8535689d2aba861e0c52e83 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 14 Apr 2020 16:28:55 +0530 Subject: [PATCH 3/4] suggested changes --- docs/unittesting.md | 25 +++++++++----- docs/unittesting_examples.md | 67 +++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/docs/unittesting.md b/docs/unittesting.md index 046ee4ad51..b2d1fdbfea 100644 --- a/docs/unittesting.md +++ b/docs/unittesting.md @@ -45,38 +45,45 @@ Remix facilitates users with various types of customizations to test a contract **1. Custom Compiler Context** -`Solidity Unit Testing` refers `Solidity Compiler` plugin for compiler configurations. One can provide customized inputs for `Compiler`, `EVM Version` & `Enable Optimization`. +`Solidity Unit Testing` refers `Solidity Compiler` plugin for compiler configurations. One can provide customized inputs for `Compiler`, `EVM Version` & `Enable Optimization` and these will be the configuration settings used for contract compilation before running unit tests. ![](images/a-unit-testing-custom-compiler-config.png) **2. Custom Transaction Context** -For a contract method interaction, prime parameters of transaction are `from` address, `value` & `gas`. Usually, we need to test method's behaviour under different values of these parameters. +For a contract method interaction, prime parameters of transaction are `from` address, `value` & `gas`. Usually, we need to test a method's behaviour under different values of these parameters. Remix provides the functionality of custom `msg.sender` & `msg.value` of transaction using method devdoc like: ``` /// #sender: account-0 /// #value: 10 -function checkSenderIs0AndValueis10 () public payable{ +function checkSenderIs0AndValueis10 () public payable { Assert.equal(msg.sender, TestsAccounts.getAccount(0), "wrong sender in checkSenderIs0AndValueis10"); Assert.equal(msg.value, 10, "wrong value in checkSenderIs0AndValueis10"); } ``` +Things to keep in mind while using custom transaction context: -**Note:** To use custom `msg.sender` functionality, `remix_accounts.sol` should be imported in your test file. +1. Parameters must be defined in devdoc of related method +2. Each parameter key should be prefixed with a hash (**#**) and end with a colon following a space (**: **) like `#sender: ` & `#value: ` +3. For now, customization is available for parameters `sender` & `value` only +4. Sender is `from` address of a transaction which is accessed using `msg.sender` inside a contract method. It should be defined in a fixed format as '**account-**' +5. `` varies from `0-2` before remix-ide release v0.10.0 and `0-9` afterwards +6. `remix_accounts.sol` must be imported in your test file to use custom `sender` +7. Value is `value` sent along with a transaction in `wei` which is accessed using `msg.value` inside a contract method. It should be a number. -Complete example can be seen in [examples](./unittesting_examples) section. +Regarding `gas`, Remix estimates the required gas for each transaction internally. Still if a contract deployment fails with `Out-of-Gas` error, it tries to redeploy it by doubling the gas. Deployment failing with double gas will show error: ```contract deployment failed after trying twice: The contract code couldn't be stored, please check your gas limit``` -Regarding `gas`, Remix estimate the required gas for each transaction internally. Still if a contract deployment fails with `Out-of-Gas` error, it tries to redeploy it by doubling the gas. Deployment failing with double gas will show error: `contract deployment failed after trying twice: The contract code couldn\'t be stored, please check your gas limit` +Various test examples can be seen in [examples](./unittesting_examples) section. Points to remember ------------------ -* A test contract can not have a method with parameters. Having one such method will show error: `Method 'methodname' can not have parameters inside a test contract` -* No. of test accounts are `3` before remix-ide release v0.10.0 and `10` afterwards -* A test file which imports `remix_accounts.sol` might not compile successfully with `Solidity Compiler` plugin but it will work fine with Solidity Unit Testing plugin +* A test contract cannot have a method with parameters. Having one such method will show error: `Method 'methodname' can not have parameters inside a test contract` +* Number of test accounts are `3` before remix-ide release v0.10.0 and `10` afterwards +* A test file which imports `remix_accounts.sol` might not compile successfully with `Solidity Compiler` plugin but it will work fine with Solidity Unit Testing plugin. Remix-tests ---------------------- diff --git a/docs/unittesting_examples.md b/docs/unittesting_examples.md index 264ca61d7a..ee57c8ceb7 100644 --- a/docs/unittesting_examples.md +++ b/docs/unittesting_examples.md @@ -88,65 +88,74 @@ Test contract/program: `Sender_test.sol` pragma solidity >=0.4.22 <0.7.0; import "remix_tests.sol"; // this import is automatically injected by Remix import "remix_accounts.sol"; -import "./Sender.sol"; +import "./sender.sol"; + // Inherit 'Sender' contract contract SenderTest is Sender { - address account0; - address account1; - address account2; + /// Define variables referring to different accounts + address acc0; + address acc1; + address acc2; /// Initiate accounts variable function beforeAll() public { - account0 = TestsAccounts.getAccount(0); - account1 = TestsAccounts.getAccount(1); - account2 = TestsAccounts.getAccount(2); + acc0 = TestsAccounts.getAccount(0); + acc1 = TestsAccounts.getAccount(1); + acc2 = TestsAccounts.getAccount(2); } /// Test if initial owner is set correctly function testInitialOwner() public { - // account-0 is default account, so current owner should be account0 - Assert.equal(getOwner(), account0, 'owner should be account-0'); + // account at zero index (account-0) is default account, so current owner should be acc0 + Assert.equal(getOwner(), acc0, 'owner should be acc0'); } - /// Update owner. This method will be called by account0 as there is not custom sender appointed + /// Update owner first time + /// This method will be called by default account(account-0) as there is no custom sender defined function updateOwnerOnce() public { - // check method call is as expected - Assert.ok(msg.sender == account0, 'caller should default account i.e. account0'); - // update owner address to account1 - updateOwner(account1); + // check method caller is as expected + Assert.ok(msg.sender == acc0, 'caller should be default account i.e. acc0'); + // update owner address to acc1 + updateOwner(acc1); // check if owner is set to expected account - Assert.equal(getOwner(), account1, 'owner should be updated to account1'); + Assert.equal(getOwner(), acc1, 'owner should be updated to acc1'); } /// Update owner again by defining custom sender - /// #sender: account-1 + /// #sender: account-1 (sender is account at index '1') function updateOwnerOnceAgain() public { // check if caller is custom and is as expected - Assert.ok(msg.sender == account1, 'caller should be custom account i.e. account1'); - // update owner address to account2. This will be successful because account1 is current owner & caller both - updateOwner(account2); + Assert.ok(msg.sender == acc1, 'caller should be custom account i.e. acc1'); + // update owner address to acc2. This will be successful because acc1 is current owner & caller both + updateOwner(acc2); // check if owner is set to expected account i.e. account2 - Assert.equal(getOwner(), account2, 'owner should be updated to account2'); + Assert.equal(getOwner(), acc2, 'owner should be updated to acc2'); } } ``` ### 3. Testing method execution -With Solidity, one can verify the changes made by a method in storage by accessing those variables out of a contract. But while testing whether method execution was successful and if execution failed, what was the reason behind it, can also be an importnat case. -Solidity introduced `try-catch` statement in version 0.6.0 which helps a lot to solve this purpose. Previously, this could be achieved using low-level call to some extent. Here is the example to test such a scenario: +With Solidity, one can directly verify the changes made by a method in storage by retrieving those variables from a contract. But testing for a successful method execution takes some strategy. Well that is not entirely true, when a test is successful - it is usually obvious why it passed. However, when a test fails, it is essential to understand why it failed. + +To help in such cases, Solidity introduced the `try-catch` statement in version `0.6.0`. Previously, we had to use low-level calls to track down what was going on. + +Here is an example test file that use both **try-catch** blocks and **low level calls**: Contract/Program to be tested: `AttendanceRegister.sol` ``` pragma solidity >=0.4.22 <0.7.0; contract AttendanceRegister { -struct Student{ - string name; - uint class; - } -event Added(string name, uint class, uint time); -mapping(uint => Student) public register; // roll number => student details + struct Student{ + string name; + uint class; + } + + event Added(string name, uint class, uint time); + + mapping(uint => Student) public register; // roll number => student details + function add(uint rollNumber, string memory name, uint class) public returns (uint256){ require(class > 0 && class <= 12, "Invalid class"); require(register[rollNumber].class == 0, "Roll number not available"); @@ -168,6 +177,7 @@ Test contract/program: `AttendanceRegister_test.sol` pragma solidity >=0.4.22 <0.7.0; import "remix_tests.sol"; // this import is automatically injected by Remix. import "./AttendanceRegister.sol"; + contract AttendanceRegisterTest { AttendanceRegister ar; @@ -278,6 +288,7 @@ Test contract/program: `Value_test.sol` pragma solidity >=0.4.22 <0.7.0; import "remix_tests.sol"; import "./Value.sol"; + contract ValueTest{ Value v; From 49dd7642fc700ecf8f26c39e9b7e8ff9639a2a12 Mon Sep 17 00:00:00 2001 From: Aniket <30843294+Aniket-Engg@users.noreply.github.com> Date: Tue, 14 Apr 2020 16:42:01 +0530 Subject: [PATCH 4/4] Update unittesting_examples.md --- docs/unittesting_examples.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/unittesting_examples.md b/docs/unittesting_examples.md index ee57c8ceb7..94b6c6fa65 100644 --- a/docs/unittesting_examples.md +++ b/docs/unittesting_examples.md @@ -88,7 +88,7 @@ Test contract/program: `Sender_test.sol` pragma solidity >=0.4.22 <0.7.0; import "remix_tests.sol"; // this import is automatically injected by Remix import "remix_accounts.sol"; -import "./sender.sol"; +import "./Sender.sol"; // Inherit 'Sender' contract contract SenderTest is Sender { @@ -326,4 +326,4 @@ contract ValueTest{ Assert.equal(v.getTokenBalance(), 30, 'token balance should be 30'); } } -``` \ No newline at end of file +```