How to directly test a Solidity Library with Hardhat/Chai - solidity

I'm trying to test a Solidity Library directly using hardhat and chaï.
This is a Library example I would like to test:
library LibTest {
function testFunc() public view returns(bool) {
return true;
}
}
and this is how I'm trying to test it.
beforeEach(async () => {
const LibTest = await ethers.getContractFactory("LibTest");
const libTest = await LibTest.deploy();
await libTest.deployed();
})
describe('Testing test()', function () {
it("is working testFunc ?", async function () {
console.log(await libTest.testFunc());
})
})
But I have the error message:
ReferenceError: libTest is not defined
I read everything I can on Chai doc and Hardhat doc but can't found any solution

I would say to use fixture and also utilize waffle and to deploy the library contract once and save the snapshot of the contract:
const {loadFixture } = require('#nomicfoundation/hardhat-network-helpers');
const {expect} = require('chai');
const {ethers, waffle} = require('hardhat');
const {deployContract} = waffle;
const LibArtifact = require('../artifacts/contracts/lib.sol/LibTest.json');
describe("Lib tests", function () {
// We define a fixture to reuse the same setup in every test.
// We use loadFixture to run this setup once, snapshot that state,
// and reset Hardhat Network to that snapshopt in every test.
async function deployOnceFixture() {
const [owner, ...otherAccounts] = await ethers.getSigners();
lib = (await deployContract(owner, LibArtifact));
return { lib, owner, otherAccounts };
}
describe("Testing test()", function () {
it("s working testFunc ?", async function () {
const { lib } = await loadFixture(deployOnceFixture);
expect(await lib.testFunc()).to.be.true;
});
});
});
Instructions to add the waffle plugin:
Hardhat-waffle
Basically, you need to install the libraries:
npm install --save-dev #nomiclabs/hardhat-waffle 'ethereum-waffle#^3.0.0' #nomiclabs/hardhat-ethers 'ethers#^5.0.0'
And then import hardhat-waffle in the hardhat-config.js file:
require("#nomiclabs/hardhat-waffle");
Also, notice I put the test file in a test directory so I needed to go back one folder to find artifacts generated by running npx hardhat compile.
For convenience I pushed the solution in a Github repo: https://github.com/Tahlil/run-solidity-lib

The best way I have found to go about this is to create a LibTest.sol contract that invokes and tests the Lib itself. And just running abstracted tests in JS/TS to invoke the LibTest contract, connecting the Lib.sol contract to it during deployment in Hardhat.
const Lib = await ethers.getContractFactory("Lib");
const lib = await Lib.deploy();
const LibTest = await ethers.getContractFactory("LibTest", {
libraries: {
Lib: lib.address,
},
});
const libTest = await LibTest.deploy();
/// later: console.log(await libTest.testLibFunc());
LibTest.sol:
import "./Lib.sol";
library LibTest {
function testLibFunc() public view returns(bool) {
bool response = Lib.testFunc();
return response;
}
}
Were you able to find a better method?

Related

Error deploying smart contract using hardhat -- Cannot read property 'sendTransaction' of null

Getting the below error while trying to deploy a smart contract from hardhat. Error details
TypeError: Cannot read property 'sendTransaction' of null
at ContractFactory.<anonymous> (C:\Collection\node_modules\#ethersproject\contracts\src.ts\index.ts:1249:38)
at step (C:\Collection\node_modules\#ethersproject\contracts\lib\index.js:48:23)
at Object.next (C:\Collection\node_modules\#ethersproject\contracts\lib\index.js:29:53)
at fulfilled (C:\Collection\node_modules\#ethersproject\contracts\lib\index.js:20:58)
Here are the config files
hardhat.config.js
require('#nomiclabs/hardhat-waffle');
require("#nomiclabs/hardhat-ethers");
require("dotenv").config();
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.2",
networks: {
mumbai: {
url: process.env.MUMBAI_URL,
account: process.env.PRIVATE_KEY
}
}
};
deploy.js
const {ethers} = require("hardhat");
async function main() {
const SuperMario = await ethers.getContractFactory("SuperMario");
const superInstance = await SuperMario.deploy("SuperMarioCollection", "SMC");
await superInstance.deployed();
console.log("contract was deployed to:", superInstance.address());
await superInstance.mint("https://ipfs.io/ipfs/XXXXXXX");
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
I am trying to deploy it using the following command
npx hardhat run scripts/deploy.js --network mumbai
thanks
Change account to accounts in the network config
found the fix. There was an error in the hardhat.config file
instead of account:, it should have been
accounts:[process.env.PRIVATE_KEY]

Mocking runtime config value with sinon

I am adding some config values before hapi server start. Application is works fine although in test I can not use config.get(). I can get around with proxyquire. So I was wondering
Is adding config file "dynamically" is bad design?
Is there a way I can use config.get() in such suitation?
Any alternative approach?
//initialize.js
const config = require('config');
async function startServe() {
const someConfigVal = await callAPIToGetSomeJSONObject();
config.dynamicValue = someConfigVal;
server.start();
}
//doSomething.js
const config = require('config');
function doesWork() {
const valFromConfig = config.dynamicValue.X;
// In test I can use proxiquire by creating config object
...
}
function doesNotWork() {
const valFromConfig = config.get('dynamicValue.X');
// Does not work with sinon mocking as this value does not exist in config when test run.
// sinon.stub(config, 'get').withArgs('dynamicValue.X').returns(someVal);
.....
}
Context: testing.
Is adding config file "dynamically" is bad design? => No. I have done it before. The test code changes configuration file: default.json in mid test to check whether function under test behaves as expected. I used several config utilities.
Is there a way I can use config.get() in such suitation? => Yes. For sinon usage, see the example below, which use mocha. You need to define the stub/mock before the function under test use it, and do not forget to restore the stub/mock. Also there is official documentation related to this: Altering configuration values for testing at runtime, but not using sinon.
const config = require('config');
const sinon = require('sinon');
const { expect } = require('chai');
// Example: simple function under test.
function other() {
const valFromConfig = config.get('dynamicValue.X');
return valFromConfig;
}
describe('Config', function () {
it ('without stub or mock.', function () {
// Config dynamicValue.X is not exist.
// Expect to throw error.
try {
other();
expect.fail('expect never get here');
} catch (error) {
expect(error.message).to.equal('Configuration property "dynamicValue.X" is not defined');
}
});
it('get using stub.', function () {
// Create stub.
const stubConfigGet = sinon.stub(config, 'get');
stubConfigGet.withArgs('dynamicValue.X').returns(false);
// Call get.
const test = other();
// Validate te result.
expect(test).to.equal(false);
expect(stubConfigGet.calledOnce).to.equal(true);
// Restore stub.
stubConfigGet.restore();
});
it('get using mock.', function () {
// Create mock.
const mockConfig = sinon.mock(config);
mockConfig.expects('get').once().withArgs('dynamicValue.X').returns(false);
// Call get.
const test = other();
// Validate te result.
expect(test).to.equal(false);
// Restore mock.
expect(mockConfig.verify()).to.equal(true);
});
});
Hope this helps.

Invalid asm.js: Invalid member of stdlib

(node:7894) V8: /var/www/html/testeth/node_modules/solc/soljson.js:3 Invalid asm.js: Invalid member of stdlib
i am making the test deploy on ganache-cli simple contract but it is showing that warning. Please help to resolve that problem.
Below the code of the "index.sol"
pragma solidity ^0.4.17;
contract testalk{
string public message;
function testalk(string initialMsg) public {
message = initialMsg;
}
function setMessage(string nwMsg) public {
message = nwMsg;
}
}
and i am testing it using "mocha" and ganache-cli provider as code below :-
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const { interface, bytecode} = require('../compile');
require('events').EventEmitter.defaultMaxListeners = 15;
let accounts;
let testeth;
beforeEach( async ()=>{
accounts = await web3.eth.getAccounts();
testeth = await new web3.eth.Contract(JSON.parse(interface))
.deploy({data: bytecode, arguments: ['Hi Alok!']})
.send({gas: '1000000',from: accounts['0']});
});
describe("testalk",() => {
it('deploy a contract', () =>{
assert.ok(testeth.options.address);
});
it('get the message', async () => {
const message = await testeth.methods.message().call();
assert.equal('Hi Alok!', message);
//console.log(message);
})
it('get the message', async () => {
await testeth.methods.setMessage("Bye Alok!").send({from: accounts[0], gas: '1000000'});
const message = await testeth.methods.message().call();
console.log(message);
});
});
I am using Ubuntu and nodejs.
I recommend you to select a newer version of the solc compiler (e.g. check Remix to see which version works with your code). Check that the version in the pragma sentence of your Solidity code is the same as the solc version you have installed with node. Check the sample usages in the solc releases and copy the JS code. I used 0.7.4 https://libraries.io/npm/solc/0.7.4
After that, you need to adapt the compile script to return the ABI and bytecode to your tests. The names must match exactly. Here I'm returning the values in JSON format so I don't have to use JSON.parse(interface) in my test file. Note that the bytecode is only the HEX value, therefore I'm returning the contract.evm.bytecode.object. Change Lottery by the name of your contract (if you have multiple contracts you want to check the docs or try a for loop).
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const lotteryPath = path.resolve(__dirname, 'contracts', 'Lottery.sol');
const source = fs.readFileSync(lotteryPath, 'utf8')
var input = {
language: 'Solidity',
sources: {
'test.sol': {
content: source
}
},
settings: {
outputSelection: {
'*': {
'*': ['*']
}
}
}
};
var output = JSON.parse(solc.compile(JSON.stringify(input)));
var contract = output.contracts['test.sol'].Lottery;
var bytecode = contract.evm.bytecode.object;
var interface = contract.abi;
module.exports = {interface, bytecode};
My test file didn't change much, but here it is:
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider());
const { interface, bytecode } = require('../compile');
let lottery;
let accounts;
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
lottery = await new web3.eth.Contract(interface)
.deploy({ data: bytecode })
.send({ from: accounts[0], gas: '6000000' });
});
describe('Lottery Contract', () => {
it('deploys a contract', () => {
assert.ok(lottery.options.address);
});
});
This can happen because of many reasons. If you are using remix ide then by default asm is disabled in chrome that could be an issue. You could be using old compiler of Solidity in some incompatible way. There could be issues with your code itself. Unless you give all specifics of the code, the environment you are using like IDE and Operating System etc., anyone can only guess the max.
Try to install solc version >= 0.4.26.
Also, make sure you've correctly installed truffle-hdwallet-provider since it's been renamed to #truffle/hdwallet-provider.
https://www.npmjs.com/package/#truffle/hdwallet-provider
I have solved the invalid member of stdlib and "before each" hook for "deploys a contract" problems by deleting the "node-modules" folder in the current directory and then reinstall the modules from terminal:
npm install --save ganache-cli mocha web3#1.0.0-beta.37 solc#0.4.17
If you have anaconda installed on your computer you should deactivate it before installing these modules by using the
conda deactivate
command. I couldn't find a solution for this on internet and I hope this helps you to solve your problem as well...

How do i install spectron and start scripting on it?

Got a recent Requirement, where i need to do Test Automation of Backend Node js application using the spectron. I would like to know what are programming skills required to approach the same
Find the Spectron documentation at https://electronjs.org/spectron
Installation
npm install --save-dev spectron
Sample test file looks like this
const Application = require('spectron').Application
const assert = require('assert')
const electronPath = require('electron')
const path = require('path')
describe('Application launch', function () {
this.timeout(10000)
beforeEach(function () {
this.app = new Application({
path: electronPath,
args: [path.join(__dirname, '..')]
})
return this.app.start()
})
afterEach(function () {
if (this.app && this.app.isRunning()) {
return this.app.stop()
}
})
it('shows an initial window', function () {
return this.app.client.getWindowCount().then(function (count) {
assert.equal(count, 1)
})
})
})
Spectron can work with any test framework. I prefer using mocha.
Clone this project for more info https://github.com/electron/spectron

What is the best approach to test a HapiJS plugin, with Lab?

What is the best way to test a HapiJS plugin, for example one plugin that add routes and handlers.
Since I have to create an instance of Hapi.Server to run the plugins, should I define all the tests from the app's root, for all the plugins ?
or
should I manage to get THE instance of Hapi.Server in my plugin's local tests ?
If I go for the second option, my server will have registered all the plugins, including those that the plugin to be tested doesn't depends on.
What is the best way to approach this ?
Thanks in advance.
If you're using Glue (and I highly recommend it), you can create a manifest variable for each test (or group of tests) you want to execute. The manifest only needs to include plugins required for that test to execute properly.
And expose some sort of init function to actually start your server. Small example:
import Lab = require("lab");
import Code = require('code');
import Path = require('path');
import Server = require('../path/to/init/server');
export const lab = Lab.script();
const it = lab.it;
const describe = lab.describe;
const config = {...};
const internals = {
manifest: {
connections: [
{
host: 'localhost',
port: 0
}
],
registrations: [
{
plugin: {
register: '../http_routes',
options: config
}
},
{
plugin: {
register: '../business_plugin',
options: config
}
}
]
},
composeOptions: {
relativeTo: 'some_path'
}
};
describe('business plugin', function () {
it('should do some business', function (done) {
Server.init(internals.manifest, internals.composeOptions, function (err, server) {
// run your tests here
});
});
});
init function:
export const init = function (manifest: any, composeOptions: any, next: (err?: any, server?: Hapi.Server) => void) {
Glue.compose(manifest, composeOptions, function (err: any, server: Hapi.Server) {
if (err) {
return next(err);
}
server.start(function (err: any) {
return next(err, server);
});
});
};