[![Build Status](https://travis-ci.org/Alkass/polish.svg?branch=master)](https://travis-ci.org/Alkass/polish) [![Crates Package Status](https://img.shields.io/crates/v/polish.svg)](https://crates.io/crates/polish) [![](https://docs.rs/polish/badge.svg)](https://docs.rs/polish) [![](https://img.shields.io/crates/d/polish.svg)](https://crates.io/crates/polish) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/AlKass/polish/blob/master/License.md)
# Polish Polish is Test-Driven Development done right [![asciicast](https://asciinema.org/a/sDVhKPAB8elO5flUB5lb7Z10g.png)](https://asciinema.org/a/sDVhKPAB8elO5flUB5lb7Z10g) ## Getting Started ### Installing the Package The `crates.io` package is kept up-to-date with all the major changes which means you can use it by simply including the following in your `Cargo.toml` under your `dependencies` section: ```yaml polish = "*" ``` > Replace `*` with the version number shown in the crates.io badge above But if you'd like to use nightly (most recent) releases, you can include the `GitHub` package repo instead: ```yaml polish = { git = "https://github.com/alkass/polish", branch = "master" } ``` ### Writing Test Cases #### single Test Cases The simplest test case can take the following form: ```rust extern crate polish; use polish::test_case::{TestRunner, TestCaseStatus, TestCase}; use polish::logger::Logger; fn my_test_case(logger: &mut Logger) -> TestCaseStatus { // TODO: Your test case code goes here TestCaseStatus::PASSED // Other valid statuses are (FAILED, SKIPPED, and UNKNOWN) } fn main() { let test_case = TestCase::new("Test Case Title", "Test Case Criteria", Box::new(my_test_case)); TestRunner::new().run_test(test_case); } ``` This produces the following: ![](screenshots/run_test.png) > The example listed above is available [here](examples/run_test.rs) You can also pass a `Rust closure` instead of a function pointer as so: ```rust extern crate polish; use polish::test_case::{TestRunner, TestCaseStatus, TestCase}; use polish::logger::Logger; fn main() { let test_case = TestCase::new("Test Case Title", "Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case code goes here TestCaseStatus::PASSED })); TestRunner::new().run_test(test_case); } ``` > The example listed above is available [here](examples/run_test_closure.rs) #### Multiple Test Cases You can run multiple test cases as follows: ```rust extern crate polish; use polish::test_case::{TestRunner, TestCaseStatus, TestCase}; use polish::logger::Logger; fn main() { let mut runner = TestRunner::new(); runner.run_test(TestCase::new("1st Test Case Title", "Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case code goes here TestCaseStatus::PASSED }))); runner.run_test(TestCase::new("2nd Test Case Title", "Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case code goes here TestCaseStatus::PASSED }))); runner.run_test(TestCase::new("3rd Test Case Title", "Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case code goes here TestCaseStatus::PASSED }))); } ``` But a more convenient way would be to pass a `Vector` of your test cases to `run_tests` as so: ```rust extern crate polish; use polish::test_case::{TestRunner, TestCaseStatus, TestCase}; use polish::logger::Logger; fn main() { let my_tests = vec![ TestCase::new("1st Test Case Title", "1st Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case goes here TestCaseStatus::PASSED })), TestCase::new("2nd Test Case Title", "2nd Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case goes here TestCaseStatus::UNKNOWN })), TestCase::new("3rd Test Case Title", "3rd Test Case Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case goes here TestCaseStatus::FAILED }))]; TestRunner::new().run_tests(my_tests); } ``` This produces the following: ![](screenshots/run_tests.png) > The example listed above is available [here](examples/run_tests.rs) #### Embedded Test Cases You may choose to have a set of test cases as part of an object to test that object itself. For that, a clean way of writing your test cases would be to implement the `Testable` trait. Following is an example: ```rust extern crate polish; use polish::test_case::{TestRunner, TestCaseStatus, TestCase, Testable}; use polish::logger::Logger; struct MyTestCase; impl Testable for MyTestCase { fn tests(self) -> Vec { vec![ TestCase::new("Some Title #1", "Testing Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case goes here TestCaseStatus::PASSED })), TestCase::new("Some Title #2", "Testing Criteria", Box::new(|logger: &mut Logger| -> TestCaseStatus { // TODO: Your test case goes here TestCaseStatus::SKIPPED }))] } } fn main() { TestRunner::new().run_tests_from_class(MyTestCase {}); } ``` This produces the following: ![](screenshots/run_tests_from_class.png) > The example listed above is available [here](examples/run_tests_from_class.rs) ### Attributes Attributes allow you to change the behaviour of how your test cases are run. For instance, by default, your `TestRunner` instance will run all your test cases regardless of whether any have failed. If you, however, want this behaviour changed, you will need to specifically tell your `TestRunner` instance to stop the process at the first failure. THIS FEATURE IS STILL WORK-IN-PROGRESS. THIS DOCUMENT WILL BE UPDATED WITH TECHNICAL DETAILS ONCE THE FEATURE IS COMPLETE. ### Logging The logger object that's passed to each test case offers 4 logging functions (`pass`, `fail`, `warn`, and `info`). Each of these functions take a `message` argument of type `String` which allows you to use the `format!` macro to format your logs, e.g.: ```rust logger.info(format!("{} + {} = {}", 1, 2, 1 + 2)); logger.pass(format!("{id}: {message}", id = "alkass", message = "this is a message")); logger.warn(format!("about to fail")); logger.fail(format!("failed with err_code: {code}", code = -1)); ``` This produces the following: ![](screenshots/logs.png) > The example listed above is available [here](examples/logs.rs) > If your test case return status is `UNKNOWN` and you've printed at least one `fail` log from within the test case function, your test case result will be marked as `FAILED`. Otherwise, your test case will be marked as `PASSED`. ## Author [Fadi Hanna Al-Kass](https://github.com/alkass)