How to test solidity smart contracts with ruby, RSpec and Etherium.rb
Tags: ruby, etherium, solidity, rspec
Published: 2022-03-30 17:15:36

---
title: How to test solidity smart contracts with ruby, RSpec and Etherium.rb
published: true
description: A small guide on how you could test your smart contracts in rspec instead of using truffle
tags: ruby, etherium, solidity, rspec
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rok9h5glu9hxpdtjx6nv.png
 
 
 
---

I recently tried testing solidity contracts with rspec and wanted to share results since I haven't really found anyone documenting the process.

## Why?

Initially, I just wanted to test whether it's possible because I like ruby and try to stick it anywhere I can. Result turned out to be pretty good, though. I haven't found any big differences with truffle suite.

## Without further ado. Naive implementation
### Prerequisites
1. Install ganache with `npm -g ganache-cli`
2. Install solidity compiler. If you're on mac you can just use `brew install solidity`. If not here is a [link to documentation](https://docs.soliditylang.org/en/v0.8.9/installing-solidity.html)

### Our smart contract
For this tutorial. We'll use a simple hello world contract. located in `contracts/greeter.sol`
```solidity
pragma solidity >= 0.8.11;

contract Greeter {
  string private _greeting = "Hello, World!";
  address public owner;

  constructor() {
    owner = msg.sender;
  }

  function greet() external view returns(string memory) {
    return _greeting;
  }

  function setSuperGreeting(string calldata greeting) external {
    require(msg.sender == owner, "Only owner");
    _greeting = greeting;
  }
}
```

As you can see it allows to read from block and write from a blockchain. Simple but good enough for our tests.

### Our test

#### Spec setup
First, we'd need to start ganache with just
```
> ganache-cli
```

Then following [etherium.rb](https://github.com/EthWorks/ethereum.rb) documentation all we need to do is

1. Create local client
```ruby
client = Ethereum::HttpClient.new('http://localhost:8545')
```

2. Create instance of our contract by providing path to a file and instance of a client
```ruby
Ethereum::Contract.create(file: "greeter.sol", client: client)
```

3. Deploy contract
```
contract.deploy_and_wait
```

Our initial setup will look like this
```ruby
require 'spec_helper.rb'

RSpec.describe 'Greeter' do
  let(:client) { Ethereum::HttpClient.new('http://localhost:8545') }
  let(:contract) { Ethereum::Contract.create(file: "contracts/greeter.sol", client: client) }

  before { contract.deploy_and_wait }
```

#### Asserts

First, let's test our greet method. To read from a blockchain with etherium.rb we'll need to use `call` like so `contract.call.method_name`. Our test case will look like this

```ruby
it 'reads greeting' do
  expect(contract.call.greet).to eq("Hello, World!")
end
```

To change state of the blockchain we need to use transaction like so `contract.transact_and_wait.method_name`.

Here is an example of our next assertion
```ruby
it 'changes message' do
  contract.transact_and_wait.set_super_greeting("Yo")

  expect(contract.call.greet).to eq("Yo")
end
```

Our whole spec will look like this

```ruby
require 'spec_helper'

RSpec.describe 'Greeter' do
  let(:client) { Ethereum::HttpClient.new('http://localhost:8545') }
  let(:contract) { Ethereum::Contract.create(file: "contracts/greeter.sol", client: client) }

  before { contract.deploy_and_wait }

  it 'sets greeting' do
    expect(contract.call.greet).to eq("Hello, World!")
  end

  it 'changes message' do
    contract.transact_and_wait.set_super_greeting("Yo")

    expect(contract.call.greet).to eq("Yo")
  end
end

```

Running
```
> bundle exec rspec

2 examples, 0 failures
```

As you can see it's super easy only a couple drawbacks.
1. Need to start ganache by hand
2. No easy access to accounts

## Next Iteration
To overcome the above drawbacks I've created a [simple gem](https://github.com/TheSmartnik/rspec-eth). It'll start ganache before the tests and provide access to accounts

### Require gem
Let's add our gem to a `Gemfile`
```ruby
gem 'rspec-eth'
```
and require it in `spec_helper.rb` with
```ruby
require 'rspec/eth'
```

### Change spec

All you need to do now to make code work is add `type: :smart_contract`. `RSpec::Eth` provides a few methods that'll make our specs even simple. We now can remove `client` and `contract`. As the gem will guess contract location by spec location.

### Updated version

```ruby
require 'spec_helper'

RSpec.describe 'Greeter', type: :smart_contract do
  before { contract.deploy_and_wait }

  it 'sets greeting' do
    expect(contract.call.greet).to eq("Hello, World!")
  end

  it 'changes message' do
    contract.transact_and_wait.set_super_greeting("Yo")

    expect(contract.call.greet).to eq("Yo")
  end
end
```

Now stop ganache if it's still running and try running specs again

```
> bundle exec rspec

2 examples, 0 failures
```

## Conclusion

It's still a work in progress and the process can be improved by adding extra configuration options and contract-specific matchers. However, if you like me love using ruby whenever you can, I believe truffle isn't the only toolset you can use for testing smart contracts