Skip to content

TestWithJava

UrsZeidler edited this page Feb 10, 2017 · 15 revisions

Test the contract code with junit

** check out the Generated Java Code section **

Thanks to the cool project https://github.com/adridadou/eth-contract-api it is possible to test the contract code via junit on a private blockchain/ProxyBlockchain, which is very nice as no blockchain sync is needed and the tests are fast and reproducible.

The code generator can produce the necessary interfaces for the contract and the corresponding junit tests.

To get all working together you will need a defined project setup. A simple example project is here.

prequisite

In addition to the basic software (see install) you need the maven plugin, as we will create a maven project which resolves all dependencies for us.

  • maven plugin for eclipse

create the project

At first create a maven project create a simple project without archetypes. and set the group and the artifact id
select maven1 select maven1 select maven1

You could now simply use the prepared archetype.

configure the project

Now we need to add the dependencies for the project :

  <dependencies>
  	<dependency>
  		<groupId>org.adridadou</groupId>
  		<artifactId>eth-contract-api</artifactId>
  		<version>0.9</version>
  	</dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
  </dependencies>

And this is all it's needed. We need junit and the eth-contract-api. May be you need also the repository where maven finds the project.

 <repository>
    <id>bintray</id>
    <url>https://dl.bintray.com/cubefriendly/maven/</url>
</repository>

configure the code generation

Now add your model and we just need to configure the code generator. As the junit test will compile the solidity code the contract code need to be on the classpath. So you need to point the generation for the contract to a directory which is on the classpath.

launchConfig-forJava1

So choose as generation directory for the contract code, for example, the src/test/resources folder which is a nice place as it is on the classpath and also it is not a java code directory.

launchConfig-forJava1

On the generate java page point the generation of the contract interfaces to a java folder, src/main/java for example, as this is the place where the normal java code should go.

You could add a package prefix which is used as a suffix to the uml packages.

For the junit tests choose the src/test/java folder as maven expect the junit tests there. Define the type mapping, when a type is missing just add it.

The best way to map an address is currently String, and not org.adridadou.ethereum.EthAddress as long as https://github.com/adridadou/eth-contract-api/issues/19 is open.

And you are ready to go.

the generated code

For each contract a corresponding interface is created, when a function return more than one value the return value is stored in a generated dataholder named Return[operation.name.toUpperFirst()/][operation.operationReturnParameters()/]. You will find more information in the wiki (https://github.com/adridadou/eth-contract-api/wiki/Complex-type-mapping). When a contract extends an other contract, the test will also extends the other contract test.

For each contract a junit test is created named [aClass.name/]Test, it contains a test method for each public function in the contract. For each package a TestSuite called All[p.name.toUpperFirst()/]TestSuite is created containing all tests of this package.

A test contains a setup of the blockchain, and the deployment of the contract.

/**
 * Setup up the blockchain. Add the 'EthereumFacadeProvider' property to use 
 * another block chain implemenation or network.
 */
@BeforeClass
public static void setup() {
	ECKey key = new ECKey();
	sender = new EthAccount(key);
	String property = System.getProperty("EthereumFacadeProvider");
	EthereumFacadeProvider efp = new StandaloneEthereumFacadeProvider();
	if(property!=null){
		if (property.equalsIgnoreCase("main")) {
			efp = new MainEthereumFacadeProvider();
		}else if (property.equalsIgnoreCase("test")) {
			efp = new TestnetEthereumFacadeProvider();
		}else if (property.equalsIgnoreCase("morden")) {
			efp = new MordenEthereumFacadeProvider();
		}else if (property.equalsIgnoreCase("rcp")) {
			RpcEthereumFacadeProvider rcp = new RpcEthereumFacadeProvider();
			String url = System.getProperty("rcp-url");
			ethereum = rcp.create(url);
			return;//as this currently breaks the hierarchy
		}
	}
	
	ethereum = efp.create();//new EthereumFacade(bcProxy);
}

You could change the used blockchain implementation via system property EthereumFacadeProvider, adding via -D when starting the testing process for example. Currently these blockchains are supported :

  • main - the main net
  • morden - for the mordem test net
  • test - for the testnet
  • rcp - to connect to an running rcp instance
    • url - this additional property defines the url of this instance

This might make sense in a continuous integration scenario.

The setup() method simply creates the infrastructure, this is done only once per test. The method prepareTest() loads the contract code from the classpath and the method createFixture() will deploy the contact and set the fixture field.

The naming schema for the test are test[operation.name.toUpperFirst()/][operation.operationParameters()/]. For example testAddEntry_string_string(). All the test functions contains protected code parts where you add your test code. The contract is available in the field fixture.

/**
 * Test method for  addEntry.
 * @throws Exception
 */
@Test
public void testAddEntry_string_string() throws Exception {
	//Start of user code testAddEntry_string_string
    assertEquals(0, fixture.count().intValue());
    fixture.addEntry("aa","jj");
    assertEquals(1, fixture.count().intValue());
    fixture.addEntry("aa","jj");
    assertEquals(2, fixture.count().intValue());
    fixture.addEntry("aa","jj");
    assertEquals(3, fixture.count().intValue());

    //End of user code
}
Clone this wiki locally