Mqttest is an MQTT server designed for unittesting clients.
Compared to a standard server like Mosquitto, Mqttest brings a CI-friendly binary, fast RC-free tests, detailed machine-readable packet dumps, network problem simulations, selectable implementaion quirks, running the server as a library, etc.
Initial development has been sponsored by Munic.
- Simplified CI image
- Automatic open port discovery (simplifies parallel test execution)
- Statically-built single-file CLI binary (no runtime deps or fancy install)
- Use as a rust library
- Decode file dumps to rust structs
- Configure and start server
- Runtime server control and client interaction using rust channels
- Return dumps and stats after each run
- Use as a library from other languages
- Verbose log file and network dump
- No need for RC-prone connection of an observer client
- Json dump of mqtt packet data and metadata (time, connection id...)
- Global or per-connection dump filename
- Flexible payload decoding
- Controllable connection behaviors:
- Ack delay
- Close connection after a number of packets or a timeout
- Go offline
- Bad packet flow (spurious/missing ack/publish)
- Override session lifetime
- Reject clients by id or password
- Different pid-assignment strategies
- Different behavior for subsequent connections
- Stop server after a number of connections
- Stop server after a max runtime
- Your useful behavior here
- Protocol support
- MQTT 3.1.1
- MQTT 5
- IPv6
- TLS
- Warn about or reject extended client identifiers
- Warn about MQTT3 idioms obsoletted by MQTT5
- Documentation highlights MQTT implementation gotchas
Some "missing" features may already be partially implemented. They will be marked as done when fully done.
- Codec-level errors (better to unittest this at the codec library level).
- Wildcard topics (at least for now - not useful ?).
- Database or clustering support (offtopic).
- High performance/scalability (though aim to be small and fast).
Install Rust >= 1.39.0 if necessary.
# Build and install the executable
cargo install --path .
# See help
mqttest -h
Mqttest starts in just a few milliseconds. You can start a server with a different behaviour for each of your client unittest. Or you can start a single instance and leave it running while you do some ad-hoc testing.
In your Cargo.toml
:
[dev-dependencies]
# MQTT test server.
mqttest = { version = "0.2.0", default-features = false }
# mqttest needs to be started from a tokio async context.
tokio = "0.2"
# At your discretion, if you want to see server logs.
env_logger = "0.7"
In your unittests (see test.rs
for more detailed examples) :
/// Boiler-plate to run and log async code from a unittest.
fn block_on<T>(f: impl Future<Output = T>) -> T {
let _ = env_logger::builder().is_test(true).parse_filters("debug").try_init();
tokio::runtime::Builder::new().basic_scheduler().enable_all().build().unwrap().block_on(f)
}
/// Example unittest. Bring your own client.
#[test]
fn connect() {
let stats = block_on(async {
// Create a server config
let conf = Conf::new().max_connect(1);
// Start the server
let srv = Mqttest::start(conf).await.expect("Failed listen").await;
// Start your client on the port that the server selected
client::start(srv.port).await.expect("Client failure");
// Wait for the server to finish
srv.finish().await.unwrap()
});
// Check run results
assert_eq!(1, stats.conn_count);
}
Required to build the binary (as opposed to the library). Enabled by default.