tests/testthat/test-fdid.R

library(fdid)

# ===========================================================================
# CRAN-facing tests
# Tiny synthetic panel (40 units × 3 periods = 120 rows).
# Uses only the two fastest estimators (did, ols1) with no bootstrap.
# Designed to run in < 5 seconds on a single core.
# ===========================================================================

set.seed(42)
n_units <- 40
times   <- 1:3

cran_data <- data.frame(
  id      = rep(seq_len(n_units), each = length(times)),
  time    = rep(times, times = n_units),
  G       = rep(rep(c(0L, 1L), n_units / 2), each = length(times)),
  outcome = rnorm(n_units * length(times)),
  covar1  = rnorm(n_units * length(times)),
  covar2  = rnorm(n_units * length(times))
)

s_cran <- fdid_prepare(
  data       = cran_data,
  Y_label    = "outcome",
  X_labels   = c("covar1", "covar2"),
  G_label    = "G",
  unit_label = "id",
  time_label = "time"
)

test_that("[CRAN] fdid_prepare returns expected structure", {
  expect_s3_class(s_cran, "tbl_df")
  expect_true("G"    %in% names(s_cran))
  expect_true("unit" %in% names(s_cran))
  expect_true(any(grepl("^Y_", names(s_cran))))
  expect_true(any(grepl("^x",  names(s_cran))))
})

test_that("[CRAN] fdid: did", {
  res <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "did", vartype = "robust")
  expect_s3_class(res, "fdid")
  expect_equal(res$method, "did")
  expect_true(is.finite(res$est$event$Estimate))
})

test_that("[CRAN] fdid: ols1", {
  res <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "ols1", vartype = "robust")
  expect_s3_class(res, "fdid")
  expect_equal(res$method, "ols1")
  expect_true(is.finite(res$est$event$Estimate))
})

test_that("[CRAN] fdid_list bundles results", {
  r1 <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "did",  vartype = "robust")
  r2 <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "ols1", vartype = "robust")
  fl <- fdid_list(r1, r2)
  expect_s3_class(fl, "fdid_list")
  expect_length(fl, 2L)
})

test_that("[CRAN] summary and print run without error", {
  res <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "did", vartype = "robust")
  expect_output(summary(res))
  expect_output(print(res))
})

test_that("[CRAN] plot.fdid: raw and dynamic run without error", {
  res <- fdid(s_cran, tr_period = 2L, ref_period = 1L, method = "ols1", vartype = "robust")
  expect_invisible(plot(res, type = "raw"))
  expect_invisible(plot(res, type = "dynamic"))
})


# ===========================================================================
# Full internal tests  (skipped on CRAN)
# Uses the real mortality dataset and all six estimators.
# ===========================================================================

data(fdid)
mortality$uniqueid <- paste(mortality$provid, mortality$countyid, sep = "-")
mortality$G <- ifelse(mortality$pczupu >= median(mortality$pczupu, na.rm = TRUE), 1, 0)

s <- fdid_prepare(
  data       = mortality,
  Y_label    = "mortality",
  X_labels   = c("avggrain", "lnpop"),
  G_label    = "G",
  unit_label = "uniqueid",
  time_label = "year"
)

TR  <- 1958
REF <- 1957

# Helper: basic structural checks on any fdid result
check_fdid <- function(res, method_name) {
  expect_s3_class(res, "fdid")
  expect_named(res, c("est", "dynamic", "raw_means", "tr_period", "ref_period",
                      "entire_period", "method", "vartype", "times", "G", "ps",
                      "call", "target.pop"),
               ignore.order = TRUE)
  expect_equal(res$method, method_name)
  expect_true(is.finite(res$est$event$Estimate),
              label = paste(method_name, "event estimate is finite"))
  expect_true(is.finite(res$est$event$Std.Error),
              label = paste(method_name, "event SE is finite"))
  expect_true(nrow(res$dynamic) > 0,
              label = paste(method_name, "dynamic estimates non-empty"))
}

# ---------------------------------------------------------------------------
test_that("fdid_prepare returns expected structure", {
  skip_on_cran()
  expect_s3_class(s, "tbl_df")
  expect_true("G"    %in% names(s))
  expect_true("unit" %in% names(s))
  expect_true(any(grepl("^Y_", names(s))))
  expect_true(any(grepl("^x",  names(s))))
})

# ---------------------------------------------------------------------------
test_that("fdid: did", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "did", vartype = "robust")
  check_fdid(res, "did")
})

test_that("fdid: ols1", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1", vartype = "robust")
  check_fdid(res, "ols1")
})

test_that("fdid: ols2", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols2", vartype = "robust")
  check_fdid(res, "ols2")
})

test_that("fdid: ebal (targets G=1)", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ebal",
              vartype = "robust", target.pop = "1")
  check_fdid(res, "ebal")
})

test_that("fdid: ipw", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ipw", vartype = "robust")
  check_fdid(res, "ipw")
  expect_false(is.null(res$ps), label = "ipw returns propensity scores")
})

test_that("fdid: aipw", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "aipw", vartype = "robust")
  check_fdid(res, "aipw")
  expect_false(is.null(res$ps), label = "aipw returns propensity scores")
})

# ---------------------------------------------------------------------------
test_that("target.pop shifts estimates for ols2", {
  skip_on_cran()
  all_ <- fdid(s, tr_period = TR, ref_period = REF, method = "ols2",
               vartype = "robust", target.pop = "all")
  att  <- fdid(s, tr_period = TR, ref_period = REF, method = "ols2",
               vartype = "robust", target.pop = "1")
  atc  <- fdid(s, tr_period = TR, ref_period = REF, method = "ols2",
               vartype = "robust", target.pop = "0")
  # Estimates need not be identical across target populations
  expect_false(isTRUE(all.equal(all_$est$event$Estimate, att$est$event$Estimate)) &&
               isTRUE(all.equal(all_$est$event$Estimate, atc$est$event$Estimate)),
               label = "ols2 target.pop changes the estimate")
})

# ---------------------------------------------------------------------------
test_that("did and ols1 are unaffected by target.pop", {
  skip_on_cran()
  r_all <- fdid(s, tr_period = TR, ref_period = REF, method = "did",
                vartype = "robust", target.pop = "all")
  r_att <- fdid(s, tr_period = TR, ref_period = REF, method = "did",
                vartype = "robust", target.pop = "1")
  expect_equal(r_all$est$event$Estimate, r_att$est$event$Estimate,
               tolerance = 1e-10,
               label = "did estimate same across target.pop")
})

# ---------------------------------------------------------------------------
test_that("entire_period restricts dynamic output", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1",
              vartype = "robust", entire_period = c(1957, 1958, 1959))
  expect_equal(sort(res$entire_period), c(1957, 1958, 1959))
  expect_equal(nrow(res$dynamic), 3L)
})

# ---------------------------------------------------------------------------
test_that("fdid_list bundles multiple results", {
  skip_on_cran()
  r1 <- fdid(s, tr_period = TR, ref_period = REF, method = "did",  vartype = "robust")
  r2 <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1", vartype = "robust")
  fl <- fdid_list(r1, r2)
  expect_s3_class(fl, "fdid_list")
  expect_length(fl, 2L)
})

# ---------------------------------------------------------------------------
test_that("summary.fdid runs without error", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1", vartype = "robust")
  expect_output(summary(res))
})

test_that("print.fdid runs without error", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1", vartype = "robust")
  expect_output(print(res))
})

# ---------------------------------------------------------------------------
test_that("plot.fdid: raw and dynamic run without error", {
  skip_on_cran()
  res <- fdid(s, tr_period = TR, ref_period = REF, method = "ols1", vartype = "robust")
  expect_invisible(plot(res, type = "raw"))
  expect_invisible(plot(res, type = "dynamic"))
})

test_that("plot.fdid: overlap requires ipw/aipw", {
  skip_on_cran()
  res_aipw <- fdid(s, tr_period = TR, ref_period = REF, method = "aipw", vartype = "robust")
  expect_invisible(plot(res_aipw, type = "overlap"))
})

Try the fdid package in your browser

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

fdid documentation built on March 23, 2026, 5:07 p.m.