Write unit tests
This tutorial guides you through writing unit tests for the states and contracts in your CorDapp. Unit tests allow you to test the individual features of your CorDapp.
You will be creating your unit tests in these directories:
- State tests -
contracts/src/test/java/com/tutorial/contracts
. - Contract tests -
contracts/src/test/java/com/tutorial/contracts
.
Learning objectives
After you’ve completed this tutorial, you will be able to write state and contract unit tests for your CorDapp.
Write state tests
This tutorial shows you how to write simple tests that check if the states have the correct parameter types. State tests can be more complex, but this example demonstrates a basic test you can implement.
You do not need to have the contract or flows created to run these state tests.
You can write all of your state tests in the same file, as shown in this tutorial, or you can create a file for each state test.
Follow these steps to write state tests:
- Create a file called
StateTests
. - Add the
StateTests
public class. - Add the
@Test
annotation. - Add a class definition for
hasFieldOfCorrectType
and throw an exception calledNoSuchFieldException
. - Fill the class with the
TemplateState
version of the test:
TemplateState.class.getDeclaredField("msg");
assert (TemplateState.class.getDeclaredField("msg").getType().equals(String.class));
Your code should now look like this:
package com.tutorial.contracts;
import com.tutorial.states.TemplateState;
import org.junit.Test;
public class StateTests {
//Mock State test check for if the state has correct parameters type
@Test
public void hasFieldOfCorrectType() throws NoSuchFieldException {
TemplateState.class.getDeclaredField("msg");
assert (TemplateState.class.getDeclaredField("msg").getType().equals(String.class));
}
Use the template test above to create another class with tests for the parameters from the AppleStamp
state:
stampDesc
-String
.issuer
-Party
.holder
-Party
.
Check your work
After you have finished, your state test is complete and your code should look like this:
package com.tutorial.contracts;
import com.tutorial.states.AppleStamp;
import com.tutorial.states.TemplateState;
import net.corda.core.identity.Party;
import org.junit.Test;
public class StateTests {
//Mock State test check for if the state has correct parameters type
@Test
public void hasFieldOfCorrectType() throws NoSuchFieldException {
TemplateState.class.getDeclaredField("msg");
assert (TemplateState.class.getDeclaredField("msg").getType().equals(String.class));
}
@Test
public void AppleStampStateHasFieldOfCorrectType() throws NoSuchFieldException {
AppleStamp.class.getDeclaredField("stampDesc");
assert (AppleStamp.class.getDeclaredField("stampDesc").getType().equals(String.class));
AppleStamp.class.getDeclaredField("issuer");
assert (AppleStamp.class.getDeclaredField("issuer").getType().equals(Party.class));
AppleStamp.class.getDeclaredField("holder");
assert (AppleStamp.class.getDeclaredField("issuer").getType().equals(Party.class));
}
}
Write a contract test
In the contract test, you will use a feature called MockServices
to fake a transaction in order to test the contract code. You must include the contract JAR with the MockServices
you create. This ensures that everything from the contract folder is available for testing. To run the contract test, you must have both the contracts and states created.
Add MockServices
and TestIdentity
Follow these steps to create the MockServices
and mock identities to use when running the test:
- Create a file called
ContractTests
. - Add the
ContractTests
public class. - Add the private function
MockServices
and reference thecom.tutorial.contracts
JAR file. - Add two
TestIdentity
s, giving them both X500 names.
After adding the MockServices
and TestIdentity
s, your code should look like this:
package com.tutorial.contracts;
import net.corda.core.identity.CordaX500Name;
import net.corda.testing.core.TestIdentity;
import net.corda.testing.node.MockServices;
import org.junit.Test;
import java.util.Arrays;
import static net.corda.testing.node.NodeTestUtils.ledger;
public class ContractTests {
private final MockServices ledgerServices = new MockServices(Arrays.asList("com.tutorial.contracts"));
TestIdentity alice = new TestIdentity(new CordaX500Name("Alice", "TestLand", "US"));
TestIdentity bob = new TestIdentity(new CordaX500Name("Alice", "TestLand", "US"));
Use the template contract test to add Apple Stamp contract tests
The template CorDapp includes the following template contract test:
@Test
public void issuerAndRecipientCannotHaveSameEmail() {
TemplateState state = new TemplateState("Hello-World",alice.getParty(),bob.getParty());
ledger(ledgerServices, l -> {
l.transaction(tx -> {
tx.input(TemplateContract.ID, state);
tx.output(TemplateContract.ID, state);
tx.command(alice.getPublicKey(), new TemplateContract.Commands.Send());
return tx.fails(); //fails because of having inputs
});
l.transaction(tx -> {
tx.output(TemplateContract.ID, state);
tx.command(alice.getPublicKey(), new TemplateContract.Commands.Send());
return tx.verifies();
});
return null;
});
}
Follow this example to write two tests that check:
- That the stamp issuance can only have one output. Call this test
StampIssuanceCanOnlyHaveOneOutput
. - That the stamp has a description. Call this test
StampMustHaveDescription
.
Check your work
After you have completed both the StampIssuanceCanOnlyHaveOneOutput
and StampMustHaveDescription
tests, your contract tests are complete and your code should look like this:
package com.tutorial.contracts;
import com.tutorial.states.AppleStamp;
import com.tutorial.states.TemplateState;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.identity.CordaX500Name;
import net.corda.testing.core.TestIdentity;
import net.corda.testing.node.MockServices;
import org.junit.Test;
import java.util.Arrays;
import static net.corda.testing.node.NodeTestUtils.ledger;
public class ContractTests {
private final MockServices ledgerServices = new MockServices(Arrays.asList("com.tutorial.contracts"));
TestIdentity alice = new TestIdentity(new CordaX500Name("Alice", "TestLand", "US"));
TestIdentity bob = new TestIdentity(new CordaX500Name("Alice", "TestLand", "US"));
//Template Tester
@Test
public void issuerAndRecipientCannotHaveSameEmail() {
TemplateState state = new TemplateState("Hello-World",alice.getParty(),bob.getParty());
ledger(ledgerServices, l -> {
l.transaction(tx -> {
tx.input(TemplateContract.ID, state);
tx.output(TemplateContract.ID, state);
tx.command(alice.getPublicKey(), new TemplateContract.Commands.Send());
return tx.fails(); //fails because of having inputs
});
l.transaction(tx -> {
tx.output(TemplateContract.ID, state);
tx.command(alice.getPublicKey(), new TemplateContract.Commands.Send());
return tx.verifies();
});
return null;
});
}
//Basket of Apple cordapp testers
@Test
public void StampIssuanceCanOnlyHaveOneOutput(){
AppleStamp stamp = new AppleStamp("FUji4072", alice.getParty(),bob.getParty(),new UniqueIdentifier());
AppleStamp stamp2 = new AppleStamp("HoneyCrispy7864", alice.getParty(),bob.getParty(),new UniqueIdentifier());
ledger(ledgerServices, l -> {
l.transaction(tx -> {
tx.output(AppleStampContract.ID, stamp);
tx.output(AppleStampContract.ID, stamp2);
tx.command(alice.getPublicKey(), new AppleStampContract.Commands.Issue());
return tx.fails(); //fails because of having inputs
});
l.transaction(tx -> {
tx.output(AppleStampContract.ID, stamp);
tx.command(alice.getPublicKey(), new AppleStampContract.Commands.Issue());
return tx.verifies();
});
return null;
});
}
@Test
public void StampMustHaveDescription(){
AppleStamp stamp = new AppleStamp("", alice.getParty(),bob.getParty(),new UniqueIdentifier());
AppleStamp stamp2 = new AppleStamp("FUji4072", alice.getParty(),bob.getParty(),new UniqueIdentifier());
ledger(ledgerServices, l -> {
l.transaction(tx -> {
tx.output(AppleStampContract.ID, stamp);
tx.command(Arrays.asList(alice.getPublicKey(),bob.getPublicKey()), new AppleStampContract.Commands.Issue());
return tx.fails(); //fails because of having inputs
});
l.transaction(tx -> {
tx.output(AppleStampContract.ID, stamp2);
tx.command(Arrays.asList(alice.getPublicKey(),bob.getPublicKey()), new AppleStampContract.Commands.Issue());
return tx.verifies();
});
return null;
});
}
}
Next steps
Now that you know how to write unit tests, learn how to run your CorDapp then write Integration tests.
Was this page helpful?
Thanks for your feedback!
Chat with us
Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.
Propose documentation improvements directly
Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.
We're sorry this page wasn't helpful. Let us know how we can make it better!
Chat with us
Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.
Create an issue
Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.
Propose documentation improvements directly
Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.