Те(к)стови работи

низове, тестване и една торба с инструменти

(най-рошавата лекция в курса (засега))

21 октомври 2021

Административни неща

Преговор

Енумерации

1 2 3 4 5 6 7 8 9 10 11
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

Message::Quit;
Message::Move { x: 3, y: 4 };
Message::Write(String::from("baba"));
Message::ChangeColor(255, 0, 0);
#[allow(path_statements)]
fn main() {
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

Message::Quit;
Message::Move { x: 3, y: 4 };
Message::Write(String::from("baba"));
Message::ChangeColor(255, 0, 0);
}

Преговор

Съпоставяне на образци (pattern matching)

1 2 3 4 5 6 7 8 9
let message = Message::Move { x: 1, y: 3 };

match message {
    Message::Quit => println!("What a quitter!"),
    Message::Move { x, y } => println!("Going to ({}, {})!", x, y),
    Message::Write(_) => println!("Pfff.. whatevs"),
    Message::ChangeColor(_, _, b) if b > 200 => println!("So much blue!"),
    _ => println!("Don't care."),
}
Going to (1, 3)!
fn main() {
#[allow(dead_code)]
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(u8, u8, u8),
}

let message = Message::Move { x: 1, y: 3 };

match message {
    Message::Quit => println!("What a quitter!"),
    Message::Move { x, y } => println!("Going to ({}, {})!", x, y),
    Message::Write(_) => println!("Pfff.. whatevs"),
    Message::ChangeColor(_, _, b) if b > 200 => println!("So much blue!"),
    _ => println!("Don't care."),
}
}

Преговор

Option и представяне на липсваща стойност

1 2 3 4
enum Option<T> {
    Some(T),
    None,
}
fn main() {
enum Option {
    Some(T),
    None,
}
}

Преговор

Refutable/Irrefutable patterns

1 2 3 4 5
let (a, b) = (1, 2);         // -> Irrefutable pattern

if let Some(val) = Some(5) { // -> Refutable pattern
    println!("Okay!");
}
Okay!
#[allow(unused_variables)]
fn main() {
let (a, b) = (1, 2);         // -> Irrefutable pattern

if let Some(val) = Some(5) { // -> Refutable pattern
    println!("Okay!");
}
}

Неща, които изненадаха всички

1 2 3 4 5 6 7 8 9
fn add_heart(s: &mut String) {
    s.push('♥');
}

fn main() {
    let mut s = String::from("♡ ");

    add_heart(&mut s);
}
fn add_heart(s: &mut String) {
    s.push('♥');
}

fn main() {
    let mut s = String::from("♡ ");

    add_heart(&mut s);
}

Неща, които изненадаха всички

1 2 3 4 5 6 7 8 9
fn add_heart(s: &mut String) {
    s.push('♥');
}

fn main() {
    let mut s = String::from("♡ ");

    add_heart(&mut s);
}
fn add_heart(s: &mut String) {
    s.push('♥');
}

fn main() {
    let mut s = String::from("♡ ");

    add_heart(&mut s);
}

Въпросът беше - можем ли да вземем референцията от лявата страна, подобно на let ref mut s = ...

Неща, които изненадаха всички

Това работи…

1 2 3 4 5 6 7 8 9
fn add_heart(ref mut s: String) {
    s.push('♥');
}

fn main() {
    let s = String::from("♡ "); // няма mut

    add_heart(s);               // хъх?
}
fn add_heart(ref mut s: String) {
    s.push('♥');
}

fn main() {
    let s = String::from("♡ "); // няма mut

    add_heart(s);               // хъх?
}

Неща, които изненадаха всички

Но се оказва, че не е толкова магическо

1 2 3 4 5 6 7 8 9 10 11
fn add_heart(ref mut s: String) {
    s.push('♥');
}

fn main() {
    let s = String::from("♡ ");

    add_heart(s);

    println!("{}", s);
}
error[E0382]: borrow of moved value: `s` --> src/bin/main_37350f7fb03ddfe3b5d79c2c40564295a4c764cb.rs:10:20 | 6 | let s = String::from("♡ "); | - move occurs because `s` has type `String`, which does not implement the `Copy` trait 7 | 8 | add_heart(s); | - value moved here 9 | 10 | println!("{}", s); | ^ value borrowed here after move For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` due to previous error
fn add_heart(ref mut s: String) {
    s.push('♥');
}

fn main() {
    let s = String::from("♡ ");

    add_heart(s);

    println!("{}", s);
}

Pattern matching на аргументи

Подобно на let, лявата страна на аргумент на функция не е име на променлива, ами е pattern.
Можем да използваме това, за да деструктурираме някоя променлива.

1 2 3 4 5 6 7 8 9 10
fn set_dimentions((width, height): (u32, u32)) {
    todo!()
}

// еквивалетно на

fn set_dimentions_2(tmp0: (u32, u32)) {
    let (width, height) = tmp0;
    todo!()
}
fn set_dimentions((width, height): (u32, u32)) {
    todo!()
}

// еквивалетно на

fn set_dimentions_2(tmp0: (u32, u32)) {
    let (width, height) = tmp0;
    todo!()
}

fn main() {}

Pattern matching на аргументи

Можем също да го използваме, за да rebind-нем променлива като mut

1 2 3 4 5 6 7 8 9 10 11 12
fn take_string(s: String) {
    let mut s = s;
    s.push_str(" ♦♦ ");

    println!("моят низ: {:?}", s);
}

fn take_string_2(mut s: String) {
    s.push_str(" ♦♦ ");

    println!("моят низ: {:?}", s);
}
fn take_string(s: String) {
    let mut s = s;
    s.push_str(" ♦♦ ");

    println!("моят низ: {:?}", s);
}

fn take_string_2(mut s: String) {
    s.push_str(" ♦♦ ");

    println!("моят низ: {:?}", s);
}

fn main() {}

Pattern matching на аргументи

В първоначалния пример реално се случва това

1 2 3 4 5 6 7 8 9 10 11
fn add_heart(ref mut s: String) {
    s.push('♥');
}

// еквивалентно на

fn add_heart_2(s: String) {
    let mut s = s;
    let ref mut s = s;
    s.push('♥');
}
fn add_heart(ref mut s: String) {
    s.push('♥');
}

// еквивалентно на

fn add_heart_2(s: String) {
    let mut s = s;
    let ref mut s = s;
    s.push('♥');
}

fn main() {}

Кодиране на низове

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Chars

Кодиране на низове

Chars

1 2 3
println!("0x{:x}", 'я' as u32);

println!("{:?}", std::char::from_u32(0x044f));
0x44f Some('я')
fn main() {
println!("0x{:x}", 'я' as u32);

println!("{:?}", std::char::from_u32(0x044f));
}

Кодиране на низове

UTF-32

Кодиране на низове

UTF-32

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Схема на кодирането

Брой байтове Първи code point Последен code point Байт 1 Байт 2 Байт 3 Байт 4
1 U+0000 U+007F 0xxxxxxx
2 U+0080 U+07FF 110xxxxx 10xxxxxx
3 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Кодиране на низове

UTF-8

Пример: (U+d55c)

Unicode code point bits:

                  11010101 01011100

UTF-8 bits:

         11101101 10010101 10011100

Кодиране на низове

UTF-8

Пример: 💣 (U+1f4a3)

Unicode code point bits:

         00000001 11110100 10100011

UTF-8 bits:

11110000 10011111 10010010 10100011

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Низове

Итерация

1 2 3 4 5 6 7 8 9 10
// bytes() връща итератор по байтовете на низа
let bytes: Vec<u8> = "Здравей! 😊".bytes().collect();

// chars() връща итератор по символите в низа
let chars: Vec<char> = "Здравей! 😊".chars().collect();

// аs_bytes() преобразува &str в &[u8]
println!("{:x?}", "Здравей! 😊".as_bytes());
println!("{:x?}", bytes);
println!("{:?}", chars);
[d0, 97, d0, b4, d1, 80, d0, b0, d0, b2, d0, b5, d0, b9, 21, 20, f0, 9f, 98, 8a] [d0, 97, d0, b4, d1, 80, d0, b0, d0, b2, d0, b5, d0, b9, 21, 20, f0, 9f, 98, 8a] ['З', 'д', 'р', 'а', 'в', 'е', 'й', '!', ' ', '😊']
fn main() {
// bytes() връща итератор по байтовете на низа
let bytes: Vec = "Здравей! 😊".bytes().collect();

// chars() връща итератор по символите в низа
let chars: Vec = "Здравей! 😊".chars().collect();

// аs_bytes() преобразува &str в &[u8]
println!("{:x?}", "Здравей! 😊".as_bytes());
println!("{:x?}", bytes);
println!("{:?}", chars);
}

Низове

Итерация

1 2 3 4 5 6
for c in "Здравей! 😊".chars() {
    let c_string: String = c.to_string();
    let c_utf8 = c_string.as_bytes();

    println!("{}: code_point={:x}, utf8={:x?}", c, c as u32, c_utf8);
}
З: code_point=417, utf8=[d0, 97] д: code_point=434, utf8=[d0, b4] р: code_point=440, utf8=[d1, 80] а: code_point=430, utf8=[d0, b0] в: code_point=432, utf8=[d0, b2] е: code_point=435, utf8=[d0, b5] й: code_point=439, utf8=[d0, b9] !: code_point=21, utf8=[21] : code_point=20, utf8=[20] 😊: code_point=1f60a, utf8=[f0, 9f, 98, 8a]
fn main() {
for c in "Здравей! 😊".chars() {
    let c_string: String = c.to_string();
    let c_utf8 = c_string.as_bytes();

    println!("{}: code_point={:x}, utf8={:x?}", c, c as u32, c_utf8);
}
}

Низове

Дължина

str::len() връща дължината на низ в брой байтове

1 2 3 4
let hi = "Здравей! 😊";

println!("{}", hi.len());
println!("{}", hi.chars().count());
20 10
fn main() {
let hi = "Здравей! 😊";

println!("{}", hi.len());
println!("{}", hi.chars().count());
}

Низове

Индексация

При взимане на резен от низ се оказват брой байтове

1 2
let sub_hi = &"Здравей! 😊"[0..6];
println!("{:?}", sub_hi);
"Здр"
fn main() {
let sub_hi = &"Здравей! 😊"[0..6];
println!("{:?}", sub_hi);
}
1 2
let sub_hi = &"Здравей! 😊"[0..3];
println!("{:?}", sub_hi);
thread 'main' panicked at 'byte index 3 is not a char boundary; it is inside 'д' (bytes 2..4) of `Здравей! 😊`', src/bin/main_2c3c4f5929c61b0516fdd13c207f4dee42a64790.rs:2:15 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn main() {
let sub_hi = &"Здравей! 😊"[0..3];
println!("{:?}", sub_hi);
}

Низове

Заключение

Низове

Заключение

Низове

Заключение

Низове

Заключение

Документация

Документация

1 2 3
fn main() {
    /// Ако мислихте, че коментарите не водят до компилационни грешки...
}
error[E0585]: found a documentation comment that doesn't document anything --> src/bin/main_db28f9dfd3554ccccfb66bc6a6fa18931a81dce1.rs:2:5 | 2 | /// Ако мислихте, че коментарите не водят до компилационни грешки... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: doc comments must come before what they document, maybe a comment was intended with `//`? For more information about this error, try `rustc --explain E0585`. error: could not compile `rust` due to previous error
fn main() {
    /// Ако мислихте, че коментарите не водят до компилационни грешки...
}

Документация

Док-коментари

Документация

Док-коментари

Документация

Док-коментари

Документация

Док-коментари

Документация

На структури и полета

1 2 3 4 5 6 7 8 9 10
/// A (half-open) range bounded inclusively below and exclusively above (start..end).
///
/// The `Range` `start..end` contains all values with `x >= start` and `x < end`.
/// It is empty unless `start < end`.
pub struct Range<Idx> {
    /// The lower bound of the range (inclusive).
    pub start: Idx,
    /// The upper bound of the range (exclusive).
    pub end: Idx,
}
#![allow(dead_code)]
/// A (half-open) range bounded inclusively below and exclusively above (start..end).
///
/// The `Range` `start..end` contains all values with `x >= start` and `x < end`.
/// It is empty unless `start < end`.
pub struct Range {
    /// The lower bound of the range (inclusive).
    pub start: Idx,
    /// The upper bound of the range (exclusive).
    pub end: Idx,
}
fn main() {}

Документация

На функции и методи

1 2 3 4 5 6 7 8 9 10 11 12 13 14
impl String {
    /// Appends a given string slice onto the end of this `String`.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let mut s = String::from("foo");
    /// s.push_str("bar");
    /// assert_eq!("foobar", s);
    /// ```
    pub fn push_str(&mut self, string: &str) {}
}
struct String;
impl String {
    /// Appends a given string slice onto the end of this `String`.
    ///
    /// Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let mut s = String::from("foo");
    /// s.push_str("bar");
    /// assert_eq!("foobar", s);
    /// ```
    pub fn push_str(&mut self, string: &str) {}
}
fn main() {}

Документация

Вътрешни док-коментари

Коментари, които започват с //!, документират елемента в който се намират.

1 2 3 4 5 6 7 8 9
mod fs {
    //! Filesystem manipulation operations.
    //!
    //! This module contains basic methods to manipulate the
    //! contents of the local filesystem. All methods in this
    //! module represent cross-platform filesystem operations.

    /* ... */
}
mod fs {
    //! Filesystem manipulation operations.
    //!
    //! This module contains basic methods to manipulate the
    //! contents of the local filesystem. All methods in this
    //! module represent cross-platform filesystem operations.

    /* ... */
}
fn main() {}

Документация

Документация

Документация

Документация

Документация

Документация

Документация

Тестване на примери

1 2 3 4 5 6
/**
 * Always returns true.
 */
public boolean isAvailable() {
    return false;
}

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

1 2 3 4 5 6 7 8 9 10
/// Converts a `char` to a digit in the given radix.
///
///
/// # Examples
///
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
pub fn to_digit(self, radix: u32) -> Option<u32>;

Документация

Тестване на примери

1 2 3 4 5 6 7 8 9 10
/// Converts a `char` to a digit in the given radix.
///
///
/// # Examples
///
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
pub fn to_digit(self, radix: u32) -> Option<u32>;
   Doc-tests example

running 1 test
test src/lib.rs - to_digit (line 8) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Тестове

Тестове

Ако си създадем проект - библиотека, cargo създава примерен код с един тест

1
cargo new --lib example

Тестове

Ако си създадем проект - библиотека, cargo създава примерен код с един тест

1
cargo new --lib example
1 2 3 4 5 6 7 8 9
// src/lib.rs

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
fn main() {}

Тестове

Тестовете са функции, анотирани с #[test]

1 2 3 4 5 6 7 8
fn add_two(x: i32) -> i32 {
    x + 2
}

#[test]
fn it_works() {
    assert_eq!(add_two(2), 4);
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[test]
fn it_works() {
    assert_eq!(add_two(2), 4);
}
fn main() {}

Тестове

1 2 3 4 5 6 7 8
#[test]
fn always_succeeds() {
}

#[test]
fn always_fails() {
    panic!(":@");
}
#![allow(dead_code)]
#[test]
fn always_succeeds() {
}

#[test]
fn always_fails() {
    panic!(":@");
}
fn main() {}

Panic

Panic

Panic

Panic

Panic

Panic

Примери

Panic

Макроси

Panic

Макроси

Panic

Макроси

Panic

Макроси

Panic

Макроси

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

1 2 3 4 5
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert!(add_two(2) == 5);
}
thread 'main' panicked at 'assertion failed: add_two(2) == 5', src/bin/main_2258266c6522f9d4e749aa7dbf59e6832f74bb89.rs:4:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert!(add_two(2) == 5);
}

Тестове

Asserts

assert_eq! и assert_ne! показват и какви са стойностите, които сравняваме

1 2 3 4 5
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert_eq!(add_two(2), 5);
}
thread 'main' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src/bin/main_b75edb5d696cf9ff5dbb5197fef7b52a9a8afdba.rs:4:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert_eq!(add_two(2), 5);
}

Тестове

Panics

За да тестваме, че при определен вход програмата гърми, има допълнителен атрибут #[should_panic]

1 2 3 4 5 6 7 8 9 10
fn connect(addr: &str) {
    // no error handling, will panic if it can't parse `addr`
    let ip_addr: Ipv4Addr = addr.parse().unwrap();
}

#[test]
#[should_panic]
fn cant_connect_to_invalid_ip() {
    connect("10.20.30.1234");
}
#![allow(dead_code)]
#![allow(unused_variables)]
use std::net::Ipv4Addr;
fn connect(addr: &str) {
    // no error handling, will panic if it can't parse `addr`
    let ip_addr: Ipv4Addr = addr.parse().unwrap();
}

#[test]
#[should_panic]
fn cant_connect_to_invalid_ip() {
    connect("10.20.30.1234");
}
fn main(){}

Тестове

Изпълнение

С cargo test се изпълняват всички тестове

     Running target/debug/deps/example-32a7ca0b7a4e165f

running 3 tests
test always_succeeds ... ok
test it_works ... ok
test always_fails ... FAILED

failures:

---- always_fails stdout ----
thread 'always_fails' panicked at ':@', src/lib.rs:16:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    always_fails

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

Организация на тестовете

Практика е тестовете да стоят в отделен модул

1 2 3 4 5 6 7 8 9 10 11 12
fn add_two(x: i32) -> i32 {
    x + 2
}

mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
#![allow(unused_imports)]
fn add_two(x: i32) -> i32 {
    x + 2
}

mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Тестове

С #[cfg(test)] тестовете се компилират само при cargo test

1 2 3 4 5 6 7 8 9 10 11 12 13
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

1 2 3 4 5 6 7 8 9 10 11 12 13
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Организация на тестовете

Unit tests

Като подмодул тестовете имат достъп до private функционалността на модула

1 2 3 4 5 6 7 8 9 10 11 12 13
pub fn add_two(x: i32) -> i32 { internal_adder(x) }

fn internal_adder(x: i32) -> i32 { x + 2 }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(internal_adder(2), 4);
    }
}
#![allow(dead_code)]
pub fn add_two(x: i32) -> i32 { internal_adder(x) }

fn internal_adder(x: i32) -> i32 { x + 2 }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(internal_adder(2), 4);
    }
}
fn main() {}

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

1 2 3 4 5 6 7
adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── adder.rs

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Въпроси