-
Notifications
You must be signed in to change notification settings - Fork 48
Suggested recommendations for test, dev env, supply chain and error handling #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
9838274
a67bbce
47c3727
e8be56f
02e20a8
8c7b07f
36c5a30
53e5e0e
ca549e3
ac24449
70527ed
0bdd824
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,125 @@ | |
|
||
## Writing tests | ||
|
||
<mark>TODO</mark>: good practices in writing tests. | ||
Rust offers two types of test built in by default: internal tests and integration tests. | ||
In this section, we will discuss these two types of test as well as a rather special type of test, which is the trait implementation test. | ||
|
||
## Fuzzing | ||
> Recommendation {{#check TEST-DRIVEN-DEV | Adopt a test-driven development's method}} | ||
> | ||
> One of the best development habits is to start development by writing the set of tests to which the functionality must respond. | ||
|
||
### Internal | ||
|
||
Internal tests define all the tests present in the `src/` folder of a Rust project. They have the great advantage of being able to test all the functions (even private ones) if they are placed in the same file as the project. | ||
|
||
|
||
> ### Recommendation {{#check TEST-UNIT | Testing the critical path of your code}} | ||
> It is important to test the entire critical path of your application. | ||
> | ||
> This way, if a future modification causes a side effect that alters its behavior, you will notice it much sooner. | ||
|
||
```rust | ||
// private function | ||
fn my_function(){ | ||
... // Your code here | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests{ | ||
#[test] | ||
fn test_my_function(){ | ||
... // Your tests here | ||
} | ||
} | ||
``` | ||
|
||
> ### Recommendation {{#check TEST-IGNORE | Limit the number of ignored tests}} | ||
> | ||
> It is recommended to limit the number of tests that will be ignored as much as possible. | ||
|
||
Rust has an attribute system that allows part of the code to be compiled only when necessary. | ||
This makes it possible to define code that will only be compiled when a particular feature is requested. | ||
|
||
One of the basic features of any project is `test`. This allows you to describe code which will only be present when the code is compiled for testing (via the `cargo test` command). | ||
|
||
To do this, add the `#[cfg(test)]` attribute to the line above the function or module concerned: | ||
```rust | ||
#[cfg(test)] | ||
mod test{ | ||
|
||
#[test] | ||
fn test_1(){} | ||
} | ||
``` | ||
|
||
> ### Rules {{#check TEST-CFG | Wrap tests in a sub-module with the attribute `#[cfg(test)]`}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should explain the rationale in particular to not leak test helpers into binaries There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have to admit I'm not sure if this is what was expected. The recommendation seems so natural to me that I find it complex to give a more detailed explanation. |
||
> | ||
> All internal tests must be wrapped in a sub-module with the `#[cfg(test)]` attribute. Similarly, any potential functions you may develop to help these tests must also have the `#[cfg(test)]` attribute. | ||
> | ||
> The use of a sub-module makes it possible to bring together all the tests and functions required for their proper execution. This makes it quick and easy to ensure that the code does not end up in the final binary or library and compromise the application's security. | ||
|
||
### Integration | ||
|
||
> Attention | ||
> | ||
> This type of test is only available for crates which are libraries. | ||
|
||
The integration tests are the set of tests in the `tests/` folder at the root of the crate. | ||
In this folder, each `*.rs` file will be compiled as a different crate and the library tested will be used as if an external project were using it. | ||
|
||
For example, if we were developing a library called `example`, we could run the following integration test: | ||
```rust | ||
use example::method_name; | ||
|
||
#[test] | ||
fn test_api(){ | ||
method_name(); | ||
} | ||
``` | ||
|
||
These tests are run at the same time as all the other tests using the following command: | ||
```bash | ||
cargo test | ||
``` | ||
|
||
> ### Rule {{#check TEST-IMPL | Check that the public behavior of the API is as expected}} | ||
> | ||
> Integration tests must ensure that the library's behavior is as expected. These tests must cover all the solution's public functions (including the import of types, functions, enums, etc.). | ||
> | ||
> This also ensures that the API is user-friendly. | ||
|
||
### Implementing a trait | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will have to check on the current best practice They more static_asserts than tests. const _: () = {
const fn test_eq_trait<X: Eq>() {}
test_eq_trait::<i32>(); // while <f32> fails
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have a clear preference on how to implement them. |
||
|
||
The example below is used to create a test to ensure that a struct or enum implements a trait. | ||
|
||
These tests are a little unusual. If positioned in a project, they can prevent the project from compiling if they are not valid. | ||
|
||
Here is an example of code used to ensure that an enum has the Send and Sync traits: | ||
|
||
```rust | ||
enum Example {} | ||
|
||
#[cfg(test)] | ||
mod test{ | ||
use super::*; | ||
|
||
fn send_sync_trait<T : Sendc + Sync>(){} | ||
|
||
#[test] | ||
fn test_traits_impl(){ | ||
send_sync_trait::<Exemple>(); | ||
} | ||
} | ||
``` | ||
|
||
> ### Recommendation {{#check TEST-TRAIT | Create tests to ensure that certain traits are implemented for structures/enums}} | ||
> | ||
> In certain contexts, it is necessary for certain struct or enum to implement particular traits. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explain why. For instance, to check that exposed API is complete. |
||
> In such cases, it is strongly recommended that you implement this type of test. | ||
|
||
<!-- ## Fuzzing | ||
|
||
### cargo-fuzz | ||
|
||
<mark>TODO</mark>: good practices in fuzzing programs or part of programs. | ||
--> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok