2020-03-15 12:39:43 -05:00
---
id: testkit
title: Testkit
---
2021-04-08 10:11:01 -05:00
## Philosophy of Testkit
2020-03-15 12:39:43 -05:00
The high level of of the bitcoin-s testkit is to mimic and provide functionality to test 3rd party applications.
There are other examples of these in the Scala ecosystem like the `akka-testkit` and `slick-testkit` .
We use this testkit to test bitcoin-s it self.
```scala mdoc:invisible
import akka.actor.ActorSystem
import org.bitcoins.core.protocol.ln.currency._
import org.bitcoins.rpc.client.common._
import org.bitcoins.testkit.rpc._
import org.bitcoins.testkit.eclair.rpc._
import scala.concurrent._
import scala.concurrent.duration._
```
### Testkit for bitcoind
This gives the ability to create and destroy `bitcoind` on the underlying operating system to test against.
Make sure you have run `sbt downloadBitcoind` before running this example, as you need access to the bitcoind binaries.
2020-08-27 14:11:24 -05:00
Our [BitcoindRpcClient ](/api/org/bitcoins/rpc/client/common/BitcoindRpcClient ) is tested with the functionality provided in the testkit.
A quick example of a useful utility method is [BitcoindRpcTestUtil.startedBitcoindRpcClient() ](/api/org/bitcoins/testkit/rpc/BitcoindRpcTestUtil ).
2020-03-15 12:39:43 -05:00
This spins up a bitcoind regtest instance on machine and generates 101 blocks on that node.
This gives you the ability to start spending money immediately with that bitcoind node.
```scala mdoc:compile-only
implicit val system = ActorSystem("bitcoind-testkit-example")
implicit val ec = system.dispatcher
//pick our bitcoind version we want to spin up
//you can pick older versions if you want
//we support versions 16-19
val bitcoindV = BitcoindVersion.V19
//create an instance
val instance = BitcoindRpcTestUtil.instance(versionOpt = Some(bitcoindV))
//now let's create an rpc client off of that instance
Implement caching of bitcoind in the walletTest,nodeTest, and partially bitcoindRpcTest project (#2792)
* Create CachedBitcoind, implement it in FundTransactionHandlingTest
* Add BaseWalletTest, extend it with BitcoinSWalletTest & BitcoinSWalletTestCachedBitcoind, add CachedBitcoinV19 and use it RescanHandlingTest
* Make ProcessBlockTest work with cached bitcoind
* Make trait for CachedBitcoindNewest for the newest version of bitcoind
* Make UTXOLifeCycleTest use cached bitcoind
* Add WalletBloom, WalletSyncTest to use cached bitcoinds
* Add WalletIntegrationTest
* Rework beforeAll() and afterAll() into the super trait like BaseWalletTest
* Add standlone BitcoindFixtures, use it in BitcoindBackendTest
* Use new BitcoindFixtures in BitcoindBlockPollingTest
* Introduce BaseNodeTest, start implementing the usage of cached bitcoinds in the nodeTest project
* Use cached bitcoind's with SpvNodeTest & SpvNodeWithWalletTest
* Fix bug on postgres with reusing database, upsert the genesis header rather than create it
* Get NeutrinoNode tests workign with cached bitcoinds
* Fix NeutrinoNodeWithWallet by destroying wallet state for Postgres
* Add teardown helper method for bitcoind
* Teardown chain project when using node fixtures since node is dependent upon the chain project.
* Turn off parallelExecution again
* Switch the parallelExecution flag to only be set on CI, so we can get better performance when running locally
* Start implementing BitcoindFixtures, use BitcoindFixturesCachedTriple on TestUtilRpcTest
* Fix compiler errors, begin implementing NodePair
* Refactor TestRpcUtilTest to use 2 bitcoinds rather than 2
* Reduce the number of bitcoinds that MultiWalletRpcTest needs from 3 -> 1
* Reduce number of bitcoinds used in WalletRpcTest from 3 -> 2
* Add some documentation
* Try to re-add parallelExecution
* Reduce the number of bitcoinds used in PsbtRpcTest from 3 -> 2
* Disable parallelExecution in Test again
* Make BitcoindV21RpcClientTest & BitcoindV20RpcClientTest reduce bitcoind usage from 2 -> 1
* Make BitcoindV19RpcClienttest reduce bitcoind usage from 2 -> 1
* Rework MempoolRpcTest to use fixtures, add BitcoindVersion to CachedBitcoindCollection
* Make sure clientAccumm has to be specified as a paramter now rather than filling in by default
* Begin parameterizing NodePair/NodeTriple to retain type information for the specific version of bitcoind that was used
* Don't implement version in super trait
* Fix docs
* Fix async issue in V21 test suite
* Append to vectors in CachedBitcoinCollection rather than replace
* Fix rebase issues
* Add scaladocs
* Fix BitcoindV18RpcClient address info test
* Implement fixtures in BitcoindV17RpcClientTest fixtures
* Cleanup v17 PsbtRpcTest
* Reduce bitcoind usage from 3 -> 1 in BitcoindV18RpcClientTest
* Remove abandon transaction test, this allows us to reduce the number of bitcoind's used in MempoolRpcTest from 3 -> 2
* Remove the requirement to inject BitcoinSAsyncFixtureTest, add it in the test traits explicitly to make things easier. Also add explicit afterAll() method to tear down both the CachedBitcoind & BitcoinSAsyncFixtureTest
* Fix missing Await.result() in BitcoindRpcTest.afterAll()
* Rework MultiWalletRpcTest to use a NodePair
* Rework BlockchainRpcTest to use fixtures
* Rework Client start()/stop() methods. Now use an AtomicBoolean to indicate when a user has requested a client to start/stop rather than sending pings to bitcoind that can fail because the conneciton pool has been shutdown in test cases
* Try my luck with turning on parallelExecution in CI again
* Revert parallelExecution, now testes do not run in parallel on CI
* Only turn off parallelExecution for bitcoindRpcTest
* Adjust build to only have bitcoindRpcTest NOT in run parallel on mac, reduce number of blocks used in BitcoindRpcTestUtil.createNodeSequence
* Run less tests in the rpc test suite as that takes the longest, move them over to node/wallet/dlc test suite on mac osx CI
* Don't run eclair tests in parallel either
* Remove CachedBitcoind from BitcoinSWalletTest
* Fix async bug in test case
* Push to github to force re-run of CI
* Push to github to force re-run of CI
* Push to github to force re-run of CI
2021-03-19 06:37:53 -05:00
val bitcoindRpcClientF = BitcoindRpcTestUtil.startedBitcoindRpcClient(instance, Vector.newBuilder)
2020-03-15 12:39:43 -05:00
//yay! it's started. Now you can run tests against this.
//let's just grab the block count for an example
val blockCountF = for {
bitcoind < - bitcoindRpcClientF
count < - bitcoind . getBlockCount
} yield {
//run a test against the block count
assert(count > 0, s"Block count was not more than zero!")
}
//when you are done, don't forget to destroy it! Otherwise it will keep running on the underlying os
val stoppedF = for {
rpc < - bitcoindRpcClientF
_ < - blockCountF
stopped < - BitcoindRpcTestUtil . stopServers ( Vector ( rpc ) )
} yield stopped
```
For more information on how the bitcoind rpc client works, see our [bitcoind rpc docs ](../rpc/bitcoind.md )
2021-04-09 09:43:22 -05:00
#### Caching bitcoind in test cases
When doing integration tests with bitcoind, you likely do not want to spin up a
new bitcoind for _every_ test that is run.
Not to fear, when using `testkit` you can use our bitcoind fixtures for your unit tests!
These will only spin up on bitcoind per test suite, rather than one bitcoind per test.
We currently have two types of fixtures available to users of this dependency
1. [Connected pairs of bitcoind nodes ](https://github.com/bitcoin-s/bitcoin-s/blob/eaac9c154c25f3bd76615ea2151092f06df6bdb4/testkit/src/main/scala/org/bitcoins/testkit/rpc/BitcoindFixtures.scala#L282 )
2. [Bitcoind nodes with funded wallets ](https://github.com/bitcoin-s/bitcoin-s/blob/eaac9c154c25f3bd76615ea2151092f06df6bdb4/testkit/src/main/scala/org/bitcoins/testkit/rpc/BitcoindFixtures.scala#L161 )
If you mixin either of those traits for your test, you will now have access to the corresponding fixture.
You can find an examples of how to use these two test fixtures
1. [Example of using a connected pair of nodes in test suite ](https://github.com/bitcoin-s/bitcoin-s/blob/32a6db930bdf849a94d92cd1de160b87845ab168/bitcoind-rpc-test/src/test/scala/org/bitcoins/rpc/common/WalletRpcTest.scala#L37 )
2. [Example of using a bitcoind with funded wallet in test suite ](https://github.com/bitcoin-s/bitcoin-s/blob/eaac9c154c25f3bd76615ea2151092f06df6bdb4/testkit/src/main/scala/org/bitcoins/testkit/rpc/BitcoindFixtures.scala#L161 )
2020-03-15 12:39:43 -05:00
### Testkit for eclair
We have similar utility methods for eclair. Eclair's testkit requires a bitcoind running (which we can spin up thanks to our bitcoind testkit).
Here is an example of spinning up an eclair lightning node, that is connected to a bitcoind and testing your lightning application.
Make sure to run `sbt downloadBitcoind downloadEclair` before running this so you have access to the underlying eclair binares
```scala mdoc:compile-only
//Steps:
//1. Open and confirm channel on the underlying blockchain (regtest)
//2. pay an invoice
//3. Await until the payment is processed
//4. assert the node has received the payment
//5. cleanup
implicit val system = ActorSystem("eclair-testkit-example")
implicit val ec = system.dispatcher
//we need a bitcoind to connect eclair nodes to
lazy val bitcoindRpcClientF: Future[BitcoindRpcClient] = {
for {
cli < - EclairRpcTestUtil . startedBitcoindRpcClient ( )
// make sure we have enough money to open channels
address < - cli . getNewAddress
_ < - cli . generateToAddress ( 200 , address )
} yield cli
}
//let's create two eclair nodes now
val clientF = for {
bitcoind < - bitcoindRpcClientF
e < - EclairRpcTestUtil . randomEclairClient ( Some ( bitcoind ) )
} yield e
val otherClientF = for {
bitcoind < - bitcoindRpcClientF
e < - EclairRpcTestUtil . randomEclairClient ( Some ( bitcoind ) )
} yield e
//great, setup done! Let's run the test
//to verify we can send a payment over the channel
for {
client < - clientF
otherClient < - otherClientF
_ < - EclairRpcTestUtil . openAndConfirmChannel ( clientF , otherClientF )
invoice < - otherClient . createInvoice ( " abc " , 50 . msats )
info < - otherClient . getInfo
_ = assert(info.nodeId == invoice.nodeId)
infos < - client . getSentInfo ( invoice . lnTags . paymentHash . hash )
_ = assert(infos.isEmpty)
paymentId < - client . payInvoice ( invoice )
_ < - EclairRpcTestUtil . awaitUntilPaymentSucceeded ( client , paymentId )
sentInfo < - client . getSentInfo ( invoice . lnTags . paymentHash . hash )
} yield {
assert(sentInfo.head.amount == 50.msats)
}
//don't forget to shutdown everything!
val stop1F = clientF.map(c => EclairRpcTestUtil.shutdown(c))
val stop2F = otherClientF.map(o => EclairRpcTestUtil.shutdown(o))
val stoppedBitcoindF = for {
bitcoind < - bitcoindRpcClientF
_ < - BitcoindRpcTestUtil . stopServers ( Vector ( bitcoind ) )
} yield ()
val resultF = for {
_ < - stop1F
_ < - stop2F
_ < - stoppedBitcoindF
_ < - system . terminate ( )
} yield ()
Await.result(resultF, 180.seconds)
```
### Other modules
You may find useful testkit functionality for other modules here
2020-08-27 14:11:24 -05:00
1. [Chain ](/api/org/bitcoins/testkit/chain/ChainUnitTest )
2. [Key Manager ](/api/org/bitcoins/testkit/keymanager/KeyManagerApiUnitTest )
3. [Wallet ](/api/org/bitcoins/testkit/wallet/BitcoinSWalletTest )
4. [Node ](/api/org/bitcoins/testkit/node/NodeUnitTest )
2020-03-15 12:39:43 -05:00
In general, you will find constructors and destructors of fixtures that can be useful when testing your applications
2021-07-01 06:34:10 -05:00
if you are using any of those modules.