1 stable release

new 1.0.0 Mar 24, 2025

#975 in #plugin

BSD-2-Clause and LGPL-3.0

31KB
270 lines

Test framework for Picodata plugin

CI

Описание

Picotest - это фреймворк для тестирования плагинов, созданных в окружении pike.

Для использования Picotest требуется выполнить следующие действия:

  • Установить pike:
cargo install picodata-pike
  • Добавить зависимости picotest и rstest в Cargo.toml плагина:
cargo add picotest --git https://github.com/picodata/picotest.git
cargo add rstest

Совместимость с Picodata

Picotest поддерживает версии Picodata, начиная с 25.1.1 и выше.

Интеграционное тестирование

Макрос #[picotest] используется для написания интеграционных тестов и может применяться как к функциям, так и к модулям.

Атрибуты макроса #[picotest]

Макрос #[picotest] поддерживает следующие аргументы:

  • path - путь до директории плагина. Значение по умолчанию: $(pwd).
  • timeout - длительность ожидания после развертывания кластера и перед запуском тестов. Указывается в секундах. Значение по умолчанию: 0 секунд.

Использование #[picotest] на функциях

При использовании макроса на функциях, picotest будет создавать кластер при каждом запуске очередного теста и удалять кластер по завершению теста.

use picotest::picotest;
use rstest::rstest;

#[picotest]
fn test_foo_bar() {
    assert_eq!("foo bar", "foo bar");
}

Использование #[picotest] на модулях

При использовании макроса на модуле picotest автоматически пометит все функции, названия которых начинаются с test_, как rstest-функции. Кластер будет создан один раз и удален после выполнения всех тестов в модуле.

use picotest::picotest;

#[picotest]
mod test_mod {

    fn test_foo() {
        assert_eq!("foo", "foo");
    }

    fn test_bar() {
        assert_eq!("bar", "bar");
    }
}

Совместимость с rstest

Макрос #[picotest] является оберткой над rstest, поэтому поддерживает использование fixture.

use picotest::picotest;

#[picotest]
mod test_mod {
    #[fixture]
    fn foo() -> String {
        "foo".to_string()
    }

    #[fixture]
    fn bar() -> String {
        "bar".to_string()
    }

    fn test_foo(foo: String) {
        assert_eq!(foo, "foo".to_string());
    }

    fn test_bar(bar: String) {
        assert_eq!(bar, "bar".to_string());
    }

    fn test_foo_bar(foo: String, bar: String) {
        assert_ne!(foo, bar);
    }
}

Запуск тестов

Запустите тесты с использованием переменной RUST_TEST_THREADS=1:

RUST_TEST_THREADS=1 cargo test

наличие переменной RUST_TEST_THREADS=1 необходимо только в том случае, если вы используете несколько модулей или функций с макросом #[picotest].

Пользовательские хуки

Picotest поддерживает работу с хуками before_all и after_all Для использования добавьте в свой Cargo.toml файл:

[dev-dependencies]
test-env-helpers = "0.2.2"

Пример:

use picotest::picotest;
use test_env_helpers::{after_all, before_all};

#[picotest]
#[before_all]
#[after_all]
mod test_mod {

    fn before_all() {
        todo!()
    }

    fn after_all() {
        todo!()
    }

    #[fixture]
    fn foo() -> String {
        "foo".to_string()
    }

    #[fixture]
    fn bar() -> String {
        "bar".to_string()
    }

    fn test_foo(foo: String) {
        assert_eq!(foo, "foo".to_string());
    }

    fn test_bar(bar: String) {
        assert_eq!(bar, "bar".to_string());
    }

    fn test_foo_bar(foo: String, bar: String) {
        assert_ne!(foo, bar);
    }
}

Создание кластера вручную

Picotest позволяет создавать и удалять кластер без использования макроса #[picotest].

use rstest::rstest;

#[rstest]
fn test_without_picotest_macro() {
    let cluster = picotest::run_cluster(".", 0);
    assert!(cluster.is_ok());
    assert!(cluster.is_ok_and(|cluster| cluster.path == "."));
}

Ограничения

  1. Параллельное исполнение тестов не поддерживается. Тесты должны запускаться последовательно, т.е. с переменной окружения RUST_TEST_THREADS=1 (issue #2)

Модульное тестирование

Макрос #[picotest_unit] используется для написания юнит-тестов для плагинов, созданных с помощью утилиты pike.

#[picotest_unit]
fn test_my_http_query() {
    let http_client = fibreq::ClientBuilder::new().build();

    let http_request = http_client.get("http://example.com").unwrap();
    let http_response = http_request.send().unwrap();

    assert!(http_response.status() == http_types::StatusCode::Ok);
}

Запуск тестов

Тесты запускаются через интерфейс cargo test с использованием переменной RUST_TEST_THREADS=1:

RUST_TEST_THREADS=1 cargo test

или опцией --test-threads=1:

cargo test -- --test-threads=1

Ограничения

  1. #[picotest_unit] не может использоваться в модуле под #[cfg(test)].

Пример неверного использования макроса:

#[cfg(test)]
mod tests {
    #[picotest_unit]
    fn test_declared_under_test_cfg() {}
}

Пример верного использования макроса:

mod tests {

    #[picotest_unit]
    fn test_is_NOT_declared_under_test_cfg() {}
}

По скольку каждый юнит-тест компилируется и линкуется в динамическую библиотеку плагина (см. Структура плагина), он не должен быть задан в конфигурации, отличной от debug. В противном случае при сборке тестов они будут проигнорированы компилятором.

  1. #[picotest_unit] не может использоваться совместно с другими атрибутами.

Все атрибуты используемые совместно с макросом будут отброшены.

В примере ниже #[should_panic] будет отброшен в процессе компиляции.

#[should_panic]
#[picotest_unit]
fn test_will_ignore_should_panic_attribute() {}
  1. Параллельное исполнение тестов не поддерживается. Тесты должны запускаться последовательно, т.е. с переменной окружения RUST_TEST_THREADS=1 (issue #2)

Dependencies

~14–25MB
~398K SLoC