created_at2018-04-22 11:42:09.444145
updated_at2019-07-29 08:53:41.430663
descriptionTest your Rust markdown documentation via Cargo
Markus Westerlind (Marwes)



Be a Rust Documentation Skeptic

Unix build status Documentation

Test your Rust Markdown via Cargo.

Getting started

Put this in Cargo.toml to add the little-skeptic dependency:

little-skeptic = "0.14"

little-skeptic = "0.14"

Also in Cargo.toml, to the [package] section add:

build = ""

That adds a build script through which you will tell Skeptic to build test cases from a set of Markdown files.

In write this to test all Rust code blocks in

extern crate little_skeptic;

fn main() {
    // generates doc tests for ``.

If you want to test multiple markdown files, you just need to build a list of filenames and supply that to generate_doc_tests. To help you, the method markdown_files_of_directory will create such a list, enumerating the markdown files in the specified directory. You can add more files to this list as you like:

extern crate little_skeptic;

use little_skeptic::*;

fn main() {
    // Add all markdown files in directory "book/".
    let mut mdbook_files = markdown_files_of_directory("book/");
    // Also add "" to the list of files.

Finally, in tests/ put the following macros to tie the generated test cases to cargo test:

include!(concat!(env!("OUT_DIR"), "/"));

Now any Rust code blocks in will be tested during cargo test.

Users' Guide

Rust Skeptic is not based on rustdoc. It behaves similarly in many cases, but not all. Here's the lowdown on the Skeptic system.

Note: this file itself is tested by Rust Skeptic. Because it is illustrating how to use markdown syntax, the markup on this document itself is funky, and so is the output below, particularly when illustrating Markdown's code fences (```rust).

You must ask for rust code blocks explicitly to get Rust testing, with ```rust. This is different from rustdoc, which assumes code blocks are Rust. The reason for this is that common Markdown parsers, like that used on GitHub, also do not assume Rust by default: you either get both Rust syntax highlighting and testing, or no Rust syntax highlighting and testing.

So the below is not tested by Skeptic.


let this_is_not_going_to_be_compiled_and_run = @all;
It doesn't really matter what's in here.


To indicate Rust code, code blocks are labeled rust:


println!("Calm your skepticism. This example is verified.");


Skeptic will interpret other words in the code block's 'info string' (which should be separated by comma, ,, to be GitHub-compatible). These words change how the test is interpreted: ignore, no_run, and should_panic.

ignore Info String

The ignore info string causes the test to be completely ignored. It will not be compiled or run during testing. This can be useful if an example is written in Rust (and you want it highlighted as such) but it is known to be incomplete (so it cannot compile as-is).


fn do_amazing_thing() -> i32 {
   // TODO: How do I do this?
   unimplemented! whatever I'm distracted, oh cookies!


no_run Info String

The no_run info string causes the example code not to be run during testing. Code marked with no_run will however still be compiled. This is useful for examples/test that may have side effects or dependencies which are not desirable in a testing situation.


fn do_amazing_thing() -> i32 {
   // TODO: How do I do this?



should_panic Info String

should_panic causes the test to only pass if it terminates because of a panic!().


assert!(1 == 100);


Skeptic Templates

Unlike rustdoc, Skeptic does not modify examples before testing by default. Skeptic examples are placed in a '.rs' file, compiled, then run.

This means that - by default - Skeptic examples require a main function, as in all the examples above. Implicit wrapping of examples in main, and custom injection of extern crate statements and crate attributes are controlled through templates.

Templates for a document are located in a separate file, that lives next to the document on the filesystem, and has the same full name as the document file, but with an additional "" template.

So for example, this file,, stores its templates in

This scheme allows the markdown to be displayed naturally by stock Markdown renderers without displaying the template itself. The weird file extension is similarly so that the templates themselves are interpreted as valid markdown.

Consider this example:

let p = PathBuf::from("foo");
println!("{:?}", p);

This example won't compile without defining main and importing PathBuf, but the example itself does not contain that boilerplate. Instead it is annotated skt-foo, for skeptic template foo, like so:


let p = PathBuf::from("foo");
println!("{:?}", p);


This tells skeptic to look in the template file for another markdown block with the same skt-foo annotation, and compose them together using a Handlebars templates with escaping disabled. Here's what the template looks like (the test code is available as test):


use std::path::PathBuf;



If it is necessary to have module global definitions such as for #[macro_use] crates the skeptic-root-template specifier can be used.


extern crate serde_json;


The old-style, document-global template

Within a document, a rust code block tagged skeptic-template will be used as the template for all examples in the doc that are not explicitly tagged.


use std::path::PathBuf;



Rustdoc-style undisplayed lines with #

Like rustdoc, skeptic will remove preceding # from any lines of code before compiling them. Hiding such lines during display requires custom support in the markdown renderer.



Commit count: 154

cargo fmt