We're using some of the lower level functions in the go-ethereum project (not geth). We've implemented our own peer protocols and are wondering how we can unit test these.

For context, we set up a P2P server and it bootstraps to other nodes, creating Peer objects as the handshakes are completed. This kicks off the protocol code:

func Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { // Protocol code here }

The specifics of our protocol aren't important, but it's essentially a request-reply handshake (similar to ping-pong). We'd like to test for things like timeouts, whether the protocol sends a "ping" request, whether it responds with a "pong" request, and that the peer connection is disconnected if a bad message is received.

I've looked at protocoltester.go and peer_test.go for inspiration, but both of these approaches would require duplicate massive amounts of private functions to get them to work.

Finally, our protocol code works. We're just looking to write unit tests to ensure that it continues to work. Any ideas?


SOLVED: It's not particularly elegant but it works really well and avoids having to tightly couple your unit tests with low-level ethereum code.

The key is to integrate a robust logging service. In this case, the log15 library included with go-ethereum serves nicely. By logging the events of interest, we can them write a custom handler and attach it from our unit testing code to ensure that the code works as intended.

By way of example, this code can live in the Run() function:

import (
   log "github.com/ethereum/go-ethereum/log"

func Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
    // Protocol code here 
    if server.Logger != nil {
                        server.Logger.Debug("Run()", "Status", "Sending AuthResp", "localnode", sn.Name, "remotenode", p.ID().String())

Next in your unit test code, you can add something like this:

// Capture logging events
logger := log.New("Test", "AuthHandshake")

var sentauthresp bool
condition := func(r *log.Record) error {
    // This implementation assumes all events contain string fields. 
    for k, v := range r.Ctx{
        if strings.Contains(v.(string), "Sending AuthResp") {
            sentauthresp = true

    return nil

// This allows you to inspect each log message

// Send the logger to your code here.

Finally, just assert that the event fired that sentauthresp (or whatever you named your variable to) has been set to true.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.