From a460362209428ce1b80bc58cac5dc8b8f1eafcdc Mon Sep 17 00:00:00 2001 From: SkylarMarvel <57526164+SkylarMarvel@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:06:05 -0600 Subject: [PATCH] Added some unit tests (#66) --- R/calc_invitro_concentration.R | 2 +- R/compute_sensitivity.R | 10 ++ R/sample_Css.R | 4 +- R/sensitivity_analysis.R | 3 +- R/simulate_exposure.R | 8 +- tests/testthat/test-check_lengths.R | 7 + tests/testthat/test-check_names.R | 10 ++ tests/testthat/test-compute_sensitivity.R | 67 +++++++++ tests/testthat/test-fit_hill.R | 37 +++++ tests/testthat/test-get_fixed_css.R | 25 ++++ tests/testthat/test-get_fixed_params.R | 27 ++-- tests/testthat/test-sample_Css.R | 1 + tests/testthat/test-sensitivity_analysis.R | 41 ++++++ tests/testthat/test-simulate_exposure.R | 63 +++++++-- tests/testthat/test-simulate_obesity.R | 15 +++ tests/testthat/test-simulate_population.R | 150 ++++++++++++++++----- 16 files changed, 406 insertions(+), 64 deletions(-) create mode 100644 tests/testthat/test-check_lengths.R create mode 100644 tests/testthat/test-check_names.R create mode 100644 tests/testthat/test-compute_sensitivity.R create mode 100644 tests/testthat/test-get_fixed_css.R create mode 100644 tests/testthat/test-sensitivity_analysis.R diff --git a/R/calc_invitro_concentration.R b/R/calc_invitro_concentration.R index 85d0a51..bc8fc88 100644 --- a/R/calc_invitro_concentration.R +++ b/R/calc_invitro_concentration.R @@ -30,5 +30,5 @@ calc_invitro_concentration <- function(D_int, C_ss = NULL) { } .calc_invitro_concentration <- function(D_int, C_ss) { - D_int * C_ss + as.matrix(D_int * C_ss) } diff --git a/R/compute_sensitivity.R b/R/compute_sensitivity.R index 9ad04ad..9f2960e 100644 --- a/R/compute_sensitivity.R +++ b/R/compute_sensitivity.R @@ -23,9 +23,16 @@ compute_sensitivity <- function(x, tp_b_mult <- x$par$resp$tp_b_mult } + if (is.null(x$age)) { + stop("GeoTox 'age' field is not set.", call. = FALSE) + } + if (vary == "age") { age <- x$age IR <- x$IR + if (is.null(IR)) { + stop("GeoTox 'IR' field is not set.", call. = FALSE) + } } else { age <- lapply(x$age, function(x) rep(stats::median(x), length.out = length(x))) @@ -34,6 +41,9 @@ compute_sensitivity <- function(x, if (vary == "C_ext") { C_ext <- x$C_ext + if (is.null(C_ext)) { + stop("GeoTox 'C_ext' field is not set.", call. = FALSE) + } } else { # Set exposure sd = NA (or 0) exposure <- lapply(x$exposure, \(x) x |> dplyr::mutate(sd = NA)) diff --git a/R/sample_Css.R b/R/sample_Css.R index faf73ec..b5485eb 100644 --- a/R/sample_Css.R +++ b/R/sample_Css.R @@ -28,8 +28,8 @@ sample_Css <- function(simulated_css, age, obesity) { stop("Names and lengths of 'age' and 'obesity' fields must be equal", call. = FALSE) } - if (length(age) == 0) { - stop("'age' and 'obesity' data has not been simulated", call. = FALSE) + if (length(age[[1]]) == 0) { + stop("'age' and 'obesity' data have not been simulated", call. = FALSE) } mapply( diff --git a/R/sensitivity_analysis.R b/R/sensitivity_analysis.R index d850cc0..ce77d2c 100644 --- a/R/sensitivity_analysis.R +++ b/R/sensitivity_analysis.R @@ -27,8 +27,7 @@ sensitivity_analysis <- function(x, stop("Input 'x' must be a GeoTox object.", call. = FALSE) } if (length(tp_b_mult) != 5) { - stop("Input 'tp_b_mult' must be a numeric vector of length 5.", - call. = FALSE) + stop("Input 'tp_b_mult' must have a length of 5.", call. = FALSE) } x$sensitivity <- list( diff --git a/R/simulate_exposure.R b/R/simulate_exposure.R index 3c02db4..adc848d 100644 --- a/R/simulate_exposure.R +++ b/R/simulate_exposure.R @@ -36,14 +36,14 @@ simulate_exposure <- function(x, n = 1e3) { if (!any(c("data.frame", "list") %in% class(x))) { - stop("x must be a data.frame or list") + stop("'x' must be a data.frame or list", call. = FALSE) } if (is.data.frame(x)) x <- list(x) if (.check_names(x, c(expos_mean, expos_sd))) { - stop("x data frames must contain columns named by 'expos_mean' and ", - "'expos_sd'") + stop("'x' data frames must contain columns named by 'expos_mean' and ", + "'expos_sd'", call. = FALSE) } lapply(x, function(df) { @@ -53,7 +53,7 @@ simulate_exposure <- function(x, # Have consistent output order out <- out[, order(colnames(out)), drop = FALSE] } else if (nrow(df) > 1) { - stop("x data frames must contain a column named by 'expos_label'", + stop("'x' data frames must contain a column named by 'expos_label'", call. = FALSE) } out diff --git a/tests/testthat/test-check_lengths.R b/tests/testthat/test-check_lengths.R new file mode 100644 index 0000000..8d897a4 --- /dev/null +++ b/tests/testthat/test-check_lengths.R @@ -0,0 +1,7 @@ +test_that("results", { + # TRUE means different lengths + expect_true(.check_lengths(list(1:5), + list(6:9))) + expect_false(.check_lengths(list(1:5), + list(6:10))) +}) diff --git a/tests/testthat/test-check_names.R b/tests/testthat/test-check_names.R new file mode 100644 index 0000000..c1060f4 --- /dev/null +++ b/tests/testthat/test-check_names.R @@ -0,0 +1,10 @@ +test_that("results", { + # TRUE means names not found + names <- "a" + expect_true(.check_names(c(), names = names)) + expect_true(.check_names(list(c()), names = names)) + expect_true(.check_names(list(c("a" = 1), + c()), names = names)) + expect_false(.check_names(c("a" = 1), names = names)) + expect_false(.check_names(list(c("a" = 1)), names = names)) +}) diff --git a/tests/testthat/test-compute_sensitivity.R b/tests/testthat/test-compute_sensitivity.R new file mode 100644 index 0000000..6a099cc --- /dev/null +++ b/tests/testthat/test-compute_sensitivity.R @@ -0,0 +1,67 @@ +test_that("bad inputs", { + + # Age field not set + expect_error(compute_sensitivity(GeoTox())) + + # IR field not set + geoTox <- GeoTox() + geoTox$age <- list(1) + expect_error(compute_sensitivity(geoTox)) + + # C_ext field not set + geoTox <- GeoTox() + geoTox$age <- list(1) + expect_error(compute_sensitivity(geoTox, vary = "C_ext")) +}) + +test_that("results", { + + # Similar to "test-sensitivity_analysis.R" + + geoTox <- GeoTox() + + expect_null(geoTox$sensitivity) + + geoTox$par$n <- 1 + geoTox$hill_params <- data.frame(resp_max = 5, + tp = 5, + tp.sd = 1, + logc_min = -1, + logc_max = 1, + logAC50 = 0, + logAC50.sd = 1) + geoTox$age <- list(2) + geoTox$IR <- list(0.5) + geoTox$obesity <- list("Normal") + geoTox$exposure <- list(data.frame(mean = 1, sd = 0)) + geoTox$C_ext <- list(1) + geoTox$css_sensitivity <- list(age = list(matrix(5)), + obesity = list(matrix(10)), + params = list(matrix(15)), + other = list(7)) + + out <- compute_sensitivity(geoTox, "age", NULL) + + expect_type(out, "list") + expect_length(out, 1) + + out <- compute_sensitivity(geoTox, "obesity", 1.1) + + expect_type(out, "list") + expect_length(out, 1) + + out <- compute_sensitivity(geoTox, "css_params", 1.2) + + expect_type(out, "list") + expect_length(out, 1) + + out <- compute_sensitivity(geoTox, "fit_params", 1.3) + + expect_type(out, "list") + expect_length(out, 1) + + out <- compute_sensitivity(geoTox, "C_ext", 1.4) + + expect_type(out, "list") + expect_length(out, 1) +}) diff --git a/tests/testthat/test-fit_hill.R b/tests/testthat/test-fit_hill.R index 506b398..32e3c83 100644 --- a/tests/testthat/test-fit_hill.R +++ b/tests/testthat/test-fit_hill.R @@ -1,3 +1,17 @@ +test_that("bad inputs", { + # Input should be a data frame + expect_error(fit_hill(c())) + # Expected required column names + expect_error(fit_hill(data.frame(x = 0, y = 0))) + # Expected optional column names + expect_error(fit_hill(data.frame(logc = c(-1, 1), + resp = c(100, 0)), + chem = "missing")) + expect_error(fit_hill(data.frame(logc = c(-1, 1), + resp = c(100, 0)), + assay = "missing")) +}) + test_that("output column names", { df <- data.frame( logc = -3:3, @@ -22,3 +36,26 @@ test_that("output column names", { expect_equal(names(fit_hill(df, chem = "chem_col", assay = "assay_col")), c("assay", "chem", colnames)) }) + +test_that("internal", { + + x <- data.frame(logc = c(-1, 0, 1), resp = c(10, 5, 0)) + + out <- .fit_hill(x) + + expect_equal(as.numeric(out$par["slope"]), + 1) + + out <- .fit_hill(x, fixed_slope = FALSE) + + expect_true(as.numeric(out$par["slope"]) > 1) + + res <- .extract_hill_params(out) + + colnames <- c("tp", "tp.sd", "logAC50", "logAC50.sd", "slope", "slope.sd", + "logc_min", "logc_max", "resp_min", "resp_max", + "AIC", "tp.sd.imputed", "logAC50.sd.imputed") + + expect_equal(names(res), + colnames) +}) diff --git a/tests/testthat/test-get_fixed_css.R b/tests/testthat/test-get_fixed_css.R new file mode 100644 index 0000000..d146783 --- /dev/null +++ b/tests/testthat/test-get_fixed_css.R @@ -0,0 +1,25 @@ +test_that("errors", { + expect_error(get_fixed_css(age = c(1, 2), obesity = c("Normal"))) +}) + +test_that("results", { + simulated_css <- list(tibble::tibble(age_min = 1, + age_median_css = 5, + weight = "Normal", + weight_median_css = 10, + css = list(c(15, 15)))) + C_ss <- list(matrix(7)) + age <- 2 + obesity <- "Normal" + + out <- get_fixed_css(simulated_css = simulated_css, + C_ss = C_ss, + age = age, + obesity = obesity) + + expect_equal(out, + list(age = list(matrix(5)), + params = list(matrix(15)), + obesity = list(matrix(10)), + other = list(7))) +}) diff --git a/tests/testthat/test-get_fixed_params.R b/tests/testthat/test-get_fixed_params.R index 0c0482a..c554d2d 100644 --- a/tests/testthat/test-get_fixed_params.R +++ b/tests/testthat/test-get_fixed_params.R @@ -9,13 +9,22 @@ test_that("expected results", { }) simulated_css <- list("chem1" = x, "chem2" = y) - expect_equal( - get_fixed_params(simulated_css, list(c(1, 27, 30), c(15, 60))), - list(matrix(c(4, 4, 4, 2, 2, 2), - ncol = 2, - dimnames = list(NULL, c("chem1", "chem2"))), - matrix(c(6, 6, 3, 3), - ncol = 2, - dimnames = list(NULL, c("chem1", "chem2")))) - ) + # Age list + out <- get_fixed_params(simulated_css, list(c(1, 27, 30), c(15, 60))) + + expect_equal(out, + list(matrix(c(4, 4, 4, 2, 2, 2), + ncol = 2, + dimnames = list(NULL, c("chem1", "chem2"))), + matrix(c(6, 6, 3, 3), + ncol = 2, + dimnames = list(NULL, c("chem1", "chem2"))))) + + # Age vector + out <- get_fixed_params(simulated_css, c(1, 27, 30)) + + expect_equal(out, + list(matrix(c(4, 4, 4, 2, 2, 2), + ncol = 2, + dimnames = list(NULL, c("chem1", "chem2"))))) }) diff --git a/tests/testthat/test-sample_Css.R b/tests/testthat/test-sample_Css.R index c077e95..7a5711a 100644 --- a/tests/testthat/test-sample_Css.R +++ b/tests/testthat/test-sample_Css.R @@ -1,6 +1,7 @@ test_that("bad inputs", { # Missing age/obesity data expect_error(sample_Css(age = c(), obesity = c())) + expect_error(sample_Css(age = NULL, obesity = NULL)) # Age/obesity data do not match lengths expect_error(sample_Css(age = 1:3, obesity = c("Normal", "Obese"))) # Age/obesity data do not match names diff --git a/tests/testthat/test-sensitivity_analysis.R b/tests/testthat/test-sensitivity_analysis.R new file mode 100644 index 0000000..5856d84 --- /dev/null +++ b/tests/testthat/test-sensitivity_analysis.R @@ -0,0 +1,41 @@ +test_that("bad inputs", { + expect_error(sensitivity_analysis(list())) + expect_error(sensitivity_analysis(GeoTox(), 1:3)) +}) + +test_that("results", { + + # Similar to "test-compute_sensitivity.R" + + geoTox <- GeoTox() + + expect_null(geoTox$sensitivity) + + geoTox$par$n <- 1 + geoTox$hill_params <- data.frame(resp_max = 5, + tp = 5, + tp.sd = 1, + logc_min = -1, + logc_max = 1, + logAC50 = 0, + logAC50.sd = 1) + geoTox$age <- list(2) + geoTox$IR <- list(0.5) + geoTox$obesity <- list("Normal") + geoTox$exposure <- list(data.frame(mean = 1, sd = 0)) + geoTox$C_ext <- list(1) + geoTox$css_sensitivity <- list(age = list(matrix(5)), + obesity = list(matrix(10)), + params = list(matrix(15)), + other = list(7)) + + geoTox <- sensitivity_analysis(geoTox, list(NULL, 1.1, 1.2, 1.3, 1.4)) + + geoTox$sensitivity + + expect_false(is.null(geoTox$sensitivity$age)) + expect_false(is.null(geoTox$sensitivity$obesity)) + expect_false(is.null(geoTox$sensitivity$css_params)) + expect_false(is.null(geoTox$sensitivity$fit_params)) + expect_false(is.null(geoTox$sensitivity$C_ext)) +}) diff --git a/tests/testthat/test-simulate_exposure.R b/tests/testthat/test-simulate_exposure.R index e1ac862..819677b 100644 --- a/tests/testthat/test-simulate_exposure.R +++ b/tests/testthat/test-simulate_exposure.R @@ -3,18 +3,30 @@ test_that("bad inputs", { expect_error(simulate_exposure(c())) # Need columns named by "expos_mean" and "expos_sd" expect_error(simulate_exposure(data.frame(x = 0, y = 0))) + expect_error(simulate_exposure(list(data.frame(x = 0, y = 0), + data.frame(mean = 0, sd = 0)))) # Need column named by "expos_label" if nrows > 1 expect_error(simulate_exposure(data.frame(mean = c(0, 0), sd = c(0, 0)))) }) -test_that("single data frame", { +test_that("single row", { - out <- simulate_exposure( - data.frame(mean = 1:3, - sd = (1:3) / 10, - casn = letters[1:3]), - n = 5) + out <- simulate_exposure(data.frame(mean = 10, sd = 1), n = 5) + + expect_type(out, "list") + expect_length(out, 1) + expect_equal(dim(out[[1]]), c(5, 1)) + expect_equal(colnames(out[[1]]), NULL) + +}) +test_that("single data frame", { + + out <- simulate_exposure(data.frame(mean = 1:3, + sd = (1:3) / 10, + casn = letters[1:3]), + n = 5) + expect_type(out, "list") expect_length(out, 1) expect_equal(dim(out[[1]]), c(5, 3)) @@ -30,7 +42,7 @@ test_that("two data frames", { casn = letters[1:3]), loc2 = data.frame(mean = 4:7, sd = 0.1, - casn = letters[4:7])), + casn = letters[c(4, 7, 6, 5)])), n = 5) expect_type(out, "list") @@ -69,9 +81,7 @@ test_that("custom column names", { test_that("internal", { - out <- .simulate_exposure(data.frame(mean = 1:3, - sd = (1:3) / 10, - casn = letters[1:3]), + out <- .simulate_exposure(data.frame(mean = 1:3, sd = (1:3) / 10), mean = "mean", sd = "sd", n = 5) @@ -82,3 +92,36 @@ test_that("internal", { expect_true(all(out >= 0)) }) + +test_that("internal special cases", { + + out <- .simulate_exposure(data.frame(mean = c(), sd = c()), + mean = "mean", + sd = "sd", + n = 5) + + # Output is a matrix, so type == "double" with 2 dimensions + expect_type(out, "double") + expect_equal(dim(out), c(5, 0)) + + out <- .simulate_exposure(data.frame(mean = 0, sd = 0), + mean = "mean", + sd = "sd", + n = 5) + + # Output is a matrix, so type == "double" with 2 dimensions + expect_type(out, "double") + expect_equal(dim(out), c(5, 1)) + expect_true(all(out == 0)) + + out <- .simulate_exposure(data.frame(mean = 1, sd = NA), + mean = "mean", + sd = "sd", + n = 5) + + # Output is a matrix, so type == "double" with 2 dimensions + expect_type(out, "double") + expect_equal(dim(out), c(5, 1)) + expect_true(all(out == 1)) + +}) diff --git a/tests/testthat/test-simulate_obesity.R b/tests/testthat/test-simulate_obesity.R index 4ab2494..1ebe18f 100644 --- a/tests/testthat/test-simulate_obesity.R +++ b/tests/testthat/test-simulate_obesity.R @@ -8,6 +8,21 @@ test_that("bad inputs", { OBESITY_SD = c(0, 0)))) }) +test_that("single row", { + + x <- data.frame(OBESITY_CrudePrev = 50, + OBESITY_SD = 5) + + out <- simulate_obesity(x, n = 5) + + expect_type(out, "list") + expect_length(out, 1) + expect_equal(names(out), NULL) + expect_equal(length(out[[1]]), 5) + expect_true(all(out[[1]] %in% c("Normal", "Obese"))) + +}) + test_that("default column names", { x <- data.frame(OBESITY_CrudePrev = c(20, 50, 80), diff --git a/tests/testthat/test-simulate_population.R b/tests/testthat/test-simulate_population.R index 6b2a07e..004ac70 100644 --- a/tests/testthat/test-simulate_population.R +++ b/tests/testthat/test-simulate_population.R @@ -1,3 +1,91 @@ +test_that("errors", { + # If IR_params is input, then age must be input or already exist + expect_error(GeoTox() |> simulate_population(IR_params = "test")) +}) + +test_that("populate fields - age", { + + geoTox <- GeoTox() + age <- data.frame(AGEGRP = 0:18, TOT_POP = 0) + age$TOT_POP[c(1, 10)] <- 100 + + expect_null(geoTox$age) + expect_null(geoTox$IR) + + geoTox <- geoTox |> simulate_population(age = age, n = 5) + + expect_false(is.null(geoTox$age)) + expect_false(is.null(geoTox$IR)) +}) + +test_that("populate fields - IR_params", { + + geoTox <- GeoTox() + IR_params <- data.frame("age" = c(20, 0, 50), + "mean" = c(0.3, 0.5, 0.2), + "sd" = 0) + + expect_null(geoTox$IR) + + geoTox$age <- list(seq(10, 90, by = 10)) + geoTox <- geoTox |> simulate_population(IR_params = IR_params) + + expect_false(is.null(geoTox$IR)) +}) + +test_that("populate fields - obesity", { + + geoTox <- GeoTox() + obesity <- data.frame(OBESITY_CrudePrev = c(20, 50, 80), + OBESITY_SD = c(5, 5, 5), + FIPS = c("c", "a", "b")) + + expect_null(geoTox$obesity) + + geoTox <- geoTox |> simulate_population(obesity = obesity, n = 5) + + expect_false(is.null(geoTox$obesity)) +}) + +test_that("populate fields - exposure", { + + geoTox <- GeoTox() + exposure <- data.frame(mean = 10, sd = 1) + + expect_null(geoTox$exposure) + expect_null(geoTox$C_ext) + + geoTox <- geoTox |> simulate_population(exposure = exposure, n = 5) + + expect_false(is.null(geoTox$exposure)) + expect_false(is.null(geoTox$C_ext)) +}) + +test_that("populate fields - simulated_css", { + + geoTox <- GeoTox() + x <- y <- expand.grid(age_min = seq(0, 50, 10), weight = c("Normal", "Obese")) + x$css <- lapply(1:nrow(x), function(i) { + rep(x$age_min[i] / 5 + as.integer(x$weight[i] == "Obese"), 2) + }) + x$age_median_css <- x$weight_median_css <- 10 + y$css <- lapply(1:nrow(y), function(i) { + rep(y$age_min[i] / 10 + as.integer(y$weight[i] == "Obese") / 2, 2) + }) + y$age_median_css <- y$weight_median_css <- 10 + simulated_css <- list("chem1" = x, "chem2" = y) + + expect_null(geoTox$C_ss) + expect_null(geoTox$css_sensitivity) + + geoTox$age <- seq(5, 50, by = 5) + geoTox$obesity <- rep("Normal", 10) + geoTox <- geoTox |> simulate_population(simulated_css = simulated_css) + + expect_false(is.null(geoTox$C_ss)) + expect_false(is.null(geoTox$css_sensitivity)) +}) + test_that("clear downstream - age", { # Simulate GeoTox age field @@ -43,24 +131,19 @@ test_that("default params", { geoTox <- GeoTox() - expect_equal( - geoTox$par, - list( - n = 1e3, - IR_params = NULL, - obesity = list(obes_prev = "OBESITY_CrudePrev", - obes_sd = "OBESITY_SD", - obes_label = "FIPS"), - exposure = list(expos_mean = "mean", - expos_sd = "sd", - expos_label = "casn"), - internal_dose = list(time = 1, - BW = 1, - scaling = 1), - resp = list(tp_b_mult = 1.5) - ) - ) - + expect_equal(geoTox$par, + list(n = 1e3, + IR_params = NULL, + obesity = list(obes_prev = "OBESITY_CrudePrev", + obes_sd = "OBESITY_SD", + obes_label = "FIPS"), + exposure = list(expos_mean = "mean", + expos_sd = "sd", + expos_label = "casn"), + internal_dose = list(time = 1, + BW = 1, + scaling = 1), + resp = list(tp_b_mult = 1.5))) }) test_that("update params", { @@ -93,22 +176,17 @@ test_that("update params", { expos_sd = expos_sd, expos_label = expos_label) - expect_equal( - geoTox$par, - list( - n = n, - IR_params = IR_params, - obesity = list(obes_prev = obes_prev, - obes_sd = obes_sd, - obes_label = obes_label), - exposure = list(expos_mean = expos_mean, - expos_sd = expos_sd, - expos_label = expos_label), - internal_dose = list(time = 1, - BW = 1, - scaling = 1), - resp = list(tp_b_mult = 1.5) - ) - ) - + expect_equal(geoTox$par, + list(n = n, + IR_params = IR_params, + obesity = list(obes_prev = obes_prev, + obes_sd = obes_sd, + obes_label = obes_label), + exposure = list(expos_mean = expos_mean, + expos_sd = expos_sd, + expos_label = expos_label), + internal_dose = list(time = 1, + BW = 1, + scaling = 1), + resp = list(tp_b_mult = 1.5))) })