Developers & Contributors
developer-guidelines.RmdWelcome
We welcome contributions to OmicsProcessing! Our highest priority is clean code and good documentation — with emphasis on good.
How to Contribute
- Fork the repository.
- Create a new issue describing your objective or planned change.
- Develop your changes on a separate branch.
- Once finished, open a pull request for review.
All pull requests must pass unit tests (UTs) and include clear documentation.
Coding Style Guidelines
- Modular design: factor each task into its own function.
-
Naming: lowercase,
snake_case, and meaningfully descriptive. - Documentation: every function must have complete roxygen2 docs, including at least one example.
- LLM-assisted cleanup: feel free to use LLMs to improve clarity, formatting, and naming suggestions.
Example function with roxygen2 documentation
#' Add Two Numbers
#'
#' This function takes two numeric values and returns their sum.
#'
#' @param x A numeric value.
#' @param y A numeric value.
#'
#' @return A numeric value representing the sum of `x` and `y`.
#' @export
#'
#' @examples
#' add_two_numbers(1, 2)
#' add_two_numbers(-5, 10)
add_two_numbers <- function(x, y) {
x + y
}Note on @export
Functions marked with @export are available to end users.
Functions without @export remain internal helpers—they can
still be tested and documented, but they are not part of the public
API.
Unit Tests (UTs)
Every function must have accompanying unit tests. Unit tests check function behavior and guide future contributors when changes are needed.
Example unit test
test_that("add_two_numbers works as expected", {
expect_equal(add_two_numbers(1, 2), 3)
expect_equal(add_two_numbers(-5, 10), 5)
expect_true(is.numeric(add_two_numbers(2, 2)))
})Writing Good UTs (mini-guide)
Good tests verify behavior (public API contracts), not implementation details. Aim for small, fast, deterministic tests.
What to cover
- Happy path: typical, correct usage.
- Edge cases: empty inputs, single row/column, NA/NaN/Inf, duplicated IDs, extreme values.
- Bad inputs: wrong types/shapes; assert errors with informative messages.
- Boundaries: off-by-one, min/max thresholds, zero/negative values.
- Stochastic steps: set seeds for reproducibility.
Expectations to prefer
- Exact equality:
expect_identical()(strictest). - Numeric comparisons:
expect_equal(..., tolerance = 1e-8)(floating point). - Types/classes:
expect_s3_class(obj, "data.frame"). - Names/columns:
expect_named(df, c("sample_id","metabolite","intensity")). - Logical properties:
expect_true(all(df$intensity >= 0)).
Errors, warnings, messages
expect_error(fn(bad_arg), class = "my_pkg_error") # classed errors
expect_warning(fn(x), "deprecated") # or a pattern
expect_message(fn(verbose = TRUE), "Completed step")Data-frame outputs
Check structure over exact values when appropriate:
out <- normalize_intensity(df, method = "median")
expect_s3_class(out, "data.frame")
expect_true(all(c("sample_id","metabolite","intensity_norm") %in% names(out)))
expect_equal(nrow(out), nrow(df))Fixtures & temporary files
- Put tiny example inputs under
tests/testthat/fixtures/(orinst/extdata/for examples). - Use
withr::local_tempdir()/local_tempfile()to avoid polluting the repo. - Load fixture paths via
testthat::test_path("fixtures", "tiny_input.csv").
Tests Directory Structure & Naming
Recommended layout
OmicsProcessing/
├─ R/
├─ tests/
│ ├─ testthat.R
│ └─ testthat/
│ ├─ test-process_data.R
│ ├─ test-normalize_intensity.R
│ ├─ test-impute_values.R
│ ├─ helper-setup.R
│ └─ fixtures/
│ ├─ tiny_input.csv
│ └─ tiny_metadata.csv
└─ ...
Files
-
tests/testthat.R:
library(testthat)
library(OmicsProcessing)
test_check("OmicsProcessing")- Unit test files live in
tests/testthat/and must start withtest-*.R.- One file per exported function or cohesive feature, e.g.,
test-normalize_intensity.R,test-impute_values.R,test-integration-process_data.R.
- One file per exported function or cohesive feature, e.g.,
- Fixtures (tiny static inputs) go under
tests/testthat/fixtures/.
Naming conventions
- Files:
test-<function_or_feature>.R - Tests: clear, behavior-oriented descriptions, e.g.,
test_that("process_data() errors on missing required columns", { ... }).