tests/testthat/test-schema.R

acs_schema <- schema(conf_data = acs_conf, start_data = acs_start)
acs_roadmap <- roadmap(conf_data = acs_conf, start_data = acs_start)

# Constructor tests ----------------------------------------------------------

test_that("Basic schema object properties", {
  
  # expect correct s3 class
  expect_s3_class(acs_schema, "schema")
  
  default_schema <- acs_schema[["col_schema"]]
  
  # expect one col_schema entry per confidential data column
  expect_equal(length(names(acs_conf)), length(names(default_schema)))
  
  # expect all default dtypes either dbl or fct
  expect_true(
    all(purrr::map_lgl(.x = default_schema, 
                       .f = \(x) { x[["dtype"]] %in% c("fct", "dbl") })))
  
  # expect levels = NULL whenever dtype != fct
  expect_true(
    all(purrr::map_lgl(
      .x = default_schema, 
      .f = \(x) { (x[["dtype"]] == "fct") | is.null(x[["levels"]]) } )))
  
  # expect non-zero na_prop values for missing-eligible columns
  expect_true(
    all(purrr::map_lgl(
      .x = names(default_schema),
      .f = \(x) { (
        (default_schema[[x]][["na_prop"]] > 0) & (x %in% c("inctot", "empstat")) | 
          (default_schema[[x]][["na_prop"]] == 0) & !(x %in% c("inctot", "empstat")) 
      ) }
    ))
  )
  
  # expect correct synth_vars
  expect_identical(
    acs_schema[["synth_vars"]],
    c("hcovany", "empstat", "classwkr", "age", 
      "famsize", "transit_time", "inctot") 
  )
  
  # expect correct no_variation properties
  expect_true(all(!acs_schema[["no_variation"]]))
  
})

test_that("schema encodes no_variation variables properly", {
  
  test_schema <- schema(conf_data = acs_conf |> 
                          dplyr::mutate(const = 1),
                        start_data = acs_start)
  
  # expect dummy variable to be flagged as having no variation
  expect_true(test_schema[["no_variation"]][["const"]])
  
})

test_that("col_schema encodes tibble dtypes", {
  
  # when manually casting columns to new tibble dtypes...
  test_schema <- schema(
    conf_data = acs_conf |> 
      dplyr::mutate(age = as.integer(age),
                    transit_time = as.logical(transit_time)),
    start_data = acs_start
  )[["col_schema"]]
  
  # ...expect that the new tibble dyptes are reflected in col_schema
  expect_equal(test_schema[["age"]][["dtype"]], "int")
  expect_equal(test_schema[["transit_time"]][["dtype"]], "lgl")
  
})

test_that("col_schema argument parsing", {
  
  # expect errors for invalid argument types
  expect_error(
    schema(conf_data = "not a data.frame", start_data = acs_start),
    regexp = "`conf_data` must be a data.frame",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, start_data = "not a data.frame"),
    regexp = "`start_data` must be a data.frame",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           col_schema = c("not", "a", "list")),
    regexp = "`col_schema`, if supplied, must be a list",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           enforce = "not a logical"),
    regexp = "`enforce` must be logical",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           coerce_to_factors = "not a logical"),
    regexp = "`coerce_to_factors` must be logical",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           coerce_to_doubles = "not a logical"),
    regexp = "`coerce_to_doubles` must be logical",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           na_factor_to_level = "not a logical"),
    regexp = "`na_factor_to_level` must be logical",
    fixed = TRUE
  )
  
  expect_error(
    schema(conf_data = acs_conf, 
           start_data = acs_start, 
           na_numeric_to_ind = "not a logical"),
    regexp = "`na_numeric_to_ind` must be logical",
    fixed = TRUE
  )
  
})

# Validator tests ------------------------------------------------------------

test_that("validate_schema expected errors", {
  
  # error if input is not a roadmap
  expect_error(
    validate_schema("not_a_roadmap"),
    regexp = "`roadmap` must be a roadmap object",
    fixed = TRUE
)
  
  # error if columns from col_schema not in conf_data
  expect_error(
    validate_schema(
      acs_roadmap |>
        update_schema(col_schema = list("not_a_column" = list("dtype" = "dbl")))
    ),
    regexp = "`col_schema` included unknown name(s) not_a_column",
    fixed = TRUE
  )
  
  # error if unsupported dtype provided
  expect_error(
    validate_schema(
      acs_roadmap |>
        update_schema(col_schema = list("gq" = list("dtype" = "notatype")))
    ),
    regexp = "`col_schema` included unsupported dtype(s) notatype",
    fixed = TRUE
  )
  
  # error if unsupported col_schema fields provided
  expect_error(
    validate_schema(
      acs_roadmap |>
        update_schema(col_schema = list("gq" = list("notafield" = "dbl")))
    ),
    regexp = "Invalid `col_schema` field names for variable(s) gq",
    fixed = TRUE
  )
  
})


test_that("validate_schema expected warning messages", {
  
  # message if data contains variable with no variation
  expect_message(
    validate_schema(
      roadmap(conf_data = acs_conf |> dplyr::mutate(novar = 1),
              start_data = acs_start)
    )
  )
  
  # message if data contains factor with empty levels 
  expect_message(
    validate_schema(
      roadmap(
        conf_data = acs_conf |> dplyr::mutate(
          hcovany = factor(hcovany, 
                           levels = c("No health insurance coverage", 
                                      "With health insurance coverage", 
                                      "Other"))),
        start_data = acs_start
      )
    )
  )
  
})


# Tidy API tests -------------------------------------------------------------

test_that("add_schema functionality", {
  
  # add a new schema to an existing roadmap
  old_roadmap <- acs_roadmap
  new_schema <- schema(conf_data = acs_conf, start_data = acs_start,
                       enforce = FALSE)
  new_roadmap <- old_roadmap |>
    add_schema(new_schema)
  
  # expect new object is a roadmap
  expect_s3_class(new_roadmap, "roadmap")
  
  # expect new object reflects new schema
  expect_identical(new_roadmap[["schema"]], new_schema)
  
})

test_that("update_schema functionality", {
  
  old_roadmap <- acs_roadmap
  new_roadmap <- old_roadmap |> 
    update_schema(col_schema = list("wgt" = list("dtype" = "int")),
                  enforce = FALSE)
  
  # expect new object is a roadmap
  expect_s3_class(new_roadmap, "roadmap")
  
  # expect new object reflects new schema
  expect_equal(
    new_roadmap[["schema"]][["col_schema"]][["wgt"]][["dtype"]], "int"
  )
  expect_equal(new_roadmap[["schema"]][["enforce"]], FALSE)
  
  # expect new object retains existing col_schema elements
  # (in this case, all but the last element, the 12th column "wgt")
  expect_identical(
    old_roadmap[["schema"]][["col_schema"]][1:11], 
    new_roadmap[["schema"]][["col_schema"]][1:11]
  )
  
  
})

test_that("reset_schema functionality", {
  
  old_roadmap <- roadmap(
    conf_data = acs_conf, 
    start_data = acs_start,
    schema = schema(conf_data = acs_conf, 
                    start_data = acs_start,
                    col_schema = list("wgt" = list("dtype" = "int")), 
                    enforce = FALSE))
  
  new_roadmap <- reset_schema(old_roadmap)
  
  # expect new object is a roadmap
  expect_s3_class(new_roadmap, "roadmap")
  
  # expect new schema is the same as the default one
  expect_identical(
    new_roadmap[["schema"]],
    schema(conf_data = acs_conf, start_data = acs_start)
  )
  
})


test_that("print.schema functionality", {
  
  test_schema <- schema(conf_data = acs_conf, start_data = acs_start)
  
  expect_output(print(
    test_schema), 
    "Schema: 12 columns"
  )
  
  expect_output(
    print(test_schema),
    paste(c(rep("fct", 7), rep("dbl", 5)), collapse=" ")
  )
  
})

Try the tidysynthesis package in your browser

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

tidysynthesis documentation built on March 17, 2026, 1:06 a.m.