tests/testthat/test-surveff.R

test_that("surveff works with binary treatment and overlap weights", {
  data <- make_test_data_binary(n = 150)

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "overlap",
    censoring_method = "weibull"
  )

  # Check output structure
  expect_s3_class(result, "surveff")
  expect_true(all(c("survival_estimates", "survival_se", "difference_estimates",
                    "difference_se", "eval_times", "treatment_levels", "n_levels",
                    "n", "included", "estimand", "weights") %in% names(result)))

  # Check dimensions
  expect_equal(ncol(result$survival_estimates), 2)  # Binary: 2 groups
  expect_equal(nrow(result$survival_estimates), length(result$eval_times))
  expect_equal(ncol(result$difference_estimates), 1)  # Binary: 1 difference

  # Check survival estimates in [0, 1]
  expect_true(all(result$survival_estimates >= 0 & result$survival_estimates <= 1))

  # Check estimand recorded
  expect_equal(result$estimand, "overlap")

  # Check treatment levels
  expect_equal(result$treatment_levels, c("A", "B"))
  expect_equal(result$n_levels, 2)
})

test_that("surveff works with binary treatment and ATE (IPTW)", {
  data <- make_test_data_binary(n = 150)

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "ATE",
    censoring_method = "weibull"
  )

  # Check runs and has correct structure
  expect_s3_class(result, "surveff")
  expect_equal(result$estimand, "ATE")

  # Check difference estimates present
  expect_false(is.null(result$difference_estimates))
  expect_equal(ncol(result$difference_estimates), 1)
})

test_that("surveff works with binary treatment and ATT", {
  data <- make_test_data_binary(n = 150)

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "ATT",
    att_group = "B",
    censoring_method = "weibull"
  )

  # Check runs and records ATT correctly
  expect_s3_class(result, "surveff")
  expect_equal(result$estimand, "ATT")

  # Check att_group recorded in weight_result
  expect_equal(result$weights %>% {attr(., "att_group")} %||%
               result$estimand, "ATT")  # Estimand should be ATT
})

test_that("surveff works with multiple treatment groups and contrast matrix", {
  data <- make_test_data_multi(n = 180)
  contrast_mat <- make_contrast_matrix()

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "ATE",
    contrast_matrix = contrast_mat,
    censoring_method = "weibull"
  )

  # Check output structure
  expect_s3_class(result, "surveff")
  expect_equal(ncol(result$survival_estimates), 3)  # 3 groups
  expect_equal(result$n_levels, 3)
  expect_equal(result$treatment_levels, c("A", "B", "C"))

  # Check contrasts match matrix
  expect_equal(ncol(result$difference_estimates), nrow(contrast_mat))  # 2 contrasts
  expect_equal(colnames(result$difference_estimates), rownames(contrast_mat))

  # Check contrast matrix stored
  expect_equal(result$contrast_matrix, contrast_mat)
})

test_that("surveff trimming works (symmetric and asymmetric)", {
  data <- make_test_data_binary(n = 150)

  # Symmetric trimming
  result_sym <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "ATE",
    trim = "symmetric",
    delta = 0.1,
    censoring_method = "weibull"
  )

  # Check some observations are trimmed (weights = 0)
  expect_true(any(result_sym$weights == 0))
  expect_true(any(!result_sym$included))
  expect_equal(length(result_sym$included), result_sym$n)

  # Asymmetric trimming
  result_asym <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "ATE",
    trim = "asymmetric",
    alpha = 0.05,
    censoring_method = "weibull"
  )

  # Check some observations are trimmed
  expect_true(any(result_asym$weights == 0))
  expect_true(any(!result_asym$included))
})

test_that("surveff works with Cox censoring and bootstrap variance", {
  data <- make_test_data_binary(n = 150)

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "overlap",
    censoring_method = "cox",
    variance_method = "bootstrap",
    B = 20,  # Small for speed
    seed = 999
  )

  # Check runs
  expect_s3_class(result, "surveff")
  expect_equal(result$censoring_method, "cox")
  expect_equal(result$variance_method, "bootstrap")

  # Check boot_result structure exists
  expect_false(is.null(result$boot_result))
  expect_true("B" %in% names(result$boot_result))
  expect_equal(result$boot_result$B, 20)
})

test_that("surveff summary and plot methods work", {
  data <- make_test_data_binary(n = 150)

  result <- surveff(
    data = data,
    ps_formula = Z ~ X1 + X2 + B1,
    censoring_formula = survival::Surv(time, event) ~ X1,
    estimand = "overlap",
    censoring_method = "weibull"
  )

  # Test summary method (prints style)
  expect_output(summary(result, max.len = 3), "Survival Effect Estimation Summary")

  # Test summary method (returns style)
  summ <- summary(result, style = "returns")
  expect_type(summ, "list")
  expect_true(all(c("survival_summary", "difference_summary") %in% names(summ)))

  # Test plot method
  expect_s3_class(plot(result, type = "surv"), "ggplot")
  expect_s3_class(plot(result, type = "survdiff"), "ggplot")
})

Try the PSsurvival package in your browser

Any scripts or data that you put into this service are public.

PSsurvival documentation built on Dec. 9, 2025, 9:07 a.m.