Appendix C — Procedure Behavior Tests
This document provides unit tests for the LogoClim NetLogo model. The tests validate procedure behavior to ensure correct functionality.
C.1 Problem
NetLogo procedures can produce unexpected results and exhibit side effects that may go undetected during development. The unit tests in this document validate procedure behavior through expectation tests, ensuring they function as intended and produce reliable results.
C.2 Methods
C.2.1 Data munging
All processes were made using the Quarto publishing system (Allaire et al., n.d.), the NetLogo environment, the R programming language (R Core Team, n.d.), and several R packages.
For data manipulation and workflow, priority was given to packages from the Tidyverse, rOpenSci and rspatial ecosystems, as well as other packages adhering to the tidy tools manifesto (Wickham, 2023).
C.2.2 NetLogo Integration
Integration with NetLogo (Wilensky, 1999) is facilitated by the logolink R package (Vartanian, 2026b). This package enables the execution of BehaviorSpace experiments directly from R.
Output is extracted in Table or Lists format, depending on the experiment configuration.
No Java dependencies are required. NetLogo bundles its own Java Runtime Environment (JRE), ensuring independent operation regardless of the system’s Java installation.
C.2.3 Continuous Integration
These tests use the latest release of NetLogo and are automated using GitHub Actions provided by the LogoActions project (Vartanian, 2026a). Each commit to the code repository triggers test execution, ensuring that errors are caught early in the development process.
C.2.4 Expectation Tests
Expectations are validated using the testthat (Wickham, 2011) and checkmate (Lang, 2017) R packages.
C.2.5 Code Style
The Tidyverse Tidy Tools Manifesto (Wickham, 2023), code style guide (Wickham, n.d.-a) and design principles (Wickham, n.d.-b) were followed to ensure consistency and enhance readability.
C.2.6 Reproducibility
The pipeline is fully reproducible and can be run again at any time. To ensure consistent results, the renv package (Ushey & Wickham, 2025) was used to manage and restore the R environment. See the README file in the code repository to learn how to run it.
C.3 Set the Environment
C.3.1 Load Packages
C.3.2 Set Initial Variables
Setting the JAVA_TOOL_OPTIONS is optional, but recommended to avoid unnecessary messages from the Java Media Framework.
Sys.setenv(JAVA_TOOL_OPTIONS = "-Dcom.sun.media.jai.disableMediaLib=true")model_path <- here("nlogox", "logoclim.nlogox")
C.4 as-list
setup_file <- create_experiment(
name = "as-list",
setup = "setup false",
go = NULL,
metrics = c(
'as-list "a"',
'as-list 1',
'as-list true',
'as-list [1 2 3]'
)
)results <-
model_path |>
run_experiment(
setup_file = setup_file
)
#> ℹ Running model
#> ✔ Running model [5.4s]
#>
#> ℹ Gathering metadata
#> ✔ Gathering metadata [23ms]
#>
#> ℹ Processing table output
#> ✔ Processing table output [15ms]
#>
#> ℹ The experiment run produced the following messages:
#>
#> Picked up JAVA_TOOL_OPTIONS: -Dcom.sun.media.jai.disableMediaLib=truetest_that("as-list", {
results |>
extract2("table") |>
pull(as_list_a) |>
expect_equal("[a]")
results |>
extract2("table") |>
pull(as_list_1) |>
expect_equal("[1]")
results |>
extract2("table") |>
pull(as_list_true) |>
expect_equal("[true]")
results |>
extract2("table") |>
pull(as_list_1_2_3) |>
expect_equal("[1 2 3]")
})
#> Test passed with 4 successes 🎊.
C.5 quartile
setup_file <- create_experiment(
name = "quartile",
setup = "setup false",
go = NULL,
metrics = c(
'quartile [0 1 2 3 4] 0',
'quartile [0 1 2 3 4] 1',
'quartile [0 1 2 3 4] 2',
'quartile [0 1 2 3 4] 3',
'quartile [0 1 2 3 4] 4',
'quartile [0 1 2 3 4] "iqr"',
'quartile [0 1 2 3 4] "length"',
'quartile [0 1 2 3 4 5] 0',
'quartile [0 1 2 3 4 5] 1',
'quartile [0 1 2 3 4 5] 2',
'quartile [0 1 2 3 4 5] 3',
'quartile [0 1 2 3 4 5] 4',
'quartile [0 1 2 3 4 5] "iqr"',
'quartile [0 1 2 3 4 5] "length"'
)
)results <-
model_path |>
run_experiment(
setup_file = setup_file
)
#> ℹ Running model
#> ✔ Running model [5.6s]
#>
#> ℹ Gathering metadata
#> ✔ Gathering metadata [19ms]
#>
#> ℹ Processing table output
#> ✔ Processing table output [9ms]
#>
#> ℹ The experiment run produced the following messages:
#>
#> Picked up JAVA_TOOL_OPTIONS: -Dcom.sun.media.jai.disableMediaLib=trueresults |>
pluck("table") |>
glimpse()
#> Rows: 1
#> Columns: 16
#> $ run_number <dbl> 1
#> $ step <dbl> 1
#> $ quartile_0_1_2_3_4_0 <dbl> 0
#> $ quartile_0_1_2_3_4_1 <dbl> 0
#> $ quartile_0_1_2_3_4_2 <dbl> 1
#> $ quartile_0_1_2_3_4_3 <dbl> 2
#> $ quartile_0_1_2_3_4_4 <dbl> 4
#> $ quartile_0_1_2_3_4_iqr <dbl> 2
#> $ quartile_0_1_2_3_4_length <dbl> 1.25
#> $ quartile_0_1_2_3_4_5_0 <dbl> 0
#> $ quartile_0_1_2_3_4_5_1 <dbl> 0
#> $ quartile_0_1_2_3_4_5_2 <dbl> 2
#> $ quartile_0_1_2_3_4_5_3 <dbl> 3
#> $ quartile_0_1_2_3_4_5_4 <dbl> 5
#> $ quartile_0_1_2_3_4_5_iqr <dbl> 3
#> $ quartile_0_1_2_3_4_5_length <dbl> 1.5test_that("quartile [0 1 2 3 4]", {
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_0) |>
expect_equal(0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_1) |>
expect_equal(0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_2) |>
expect_equal(1)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_3) |>
expect_equal(2)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_4) |>
expect_equal(4)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_iqr) |>
expect_equal(2 - 0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_length) |>
expect_equal(5 / 4)
})
#> Test passed with 7 successes 🌈.test_that("quartile [0 1 2 3 4 5]", {
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_0) |>
expect_equal(0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_1) |>
expect_equal(0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_2) |>
expect_equal(2)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_3) |>
expect_equal(3)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_4) |>
expect_equal(5)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_iqr) |>
expect_equal(3 - 0)
results |>
extract2("table") |>
pull(quartile_0_1_2_3_4_5_length) |>
expect_equal(6 / 4)
})
#> Test passed with 7 successes 🎉.
C.6 single-quote
setup_file <- create_experiment(
name = "single-quote",
setup = "setup false",
go = NULL,
metrics = c(
'single-quote 1',
'single-quote [1 2 3]'
)
)results <-
model_path |>
run_experiment(
setup_file = setup_file
)
#> ℹ Running model
#> ✔ Running model [5.4s]
#>
#> ℹ Gathering metadata
#> ✔ Gathering metadata [9ms]
#>
#> ℹ Processing table output
#> ✔ Processing table output [8ms]
#>
#> ℹ The experiment run produced the following messages:
#>
#> Picked up JAVA_TOOL_OPTIONS: -Dcom.sun.media.jai.disableMediaLib=truetest_that("single-quote", {
results |>
extract2("table") |>
pull(single_quote_1) |>
expect_equal("'1'")
results |>
extract2("table") |>
pull(single_quote_1_2_3) |>
expect_equal("['1' '2' '3']")
})
#> Test passed with 2 successes 😀.
C.7 str-to-num-month
setup_file <- create_experiment(
name = "str-to-num-month",
setup = "setup false",
go = NULL,
metrics = c(
'str-to-num-month "January"',
'str-to-num-month "July"',
'str-to-num-month "December"'
)
)results <-
model_path |>
run_experiment(
setup_file = setup_file
)
#> ℹ Running model
#> ✔ Running model [5.5s]
#>
#> ℹ Gathering metadata
#> ✔ Gathering metadata [9ms]
#>
#> ℹ Processing table output
#> ✔ Processing table output [8ms]
#>
#> ℹ The experiment run produced the following messages:
#>
#> Picked up JAVA_TOOL_OPTIONS: -Dcom.sun.media.jai.disableMediaLib=truetest_that("str-to-num-month", {
results |>
extract2("table") |>
pull(str_to_num_month_january) |>
expect_equal(1)
results |>
extract2("table") |>
pull(str_to_num_month_july) |>
expect_equal(7)
results |>
extract2("table") |>
pull(str_to_num_month_december) |>
expect_equal(12)
})
#> Test passed with 3 successes 🥳.
C.8 unique-outliers
setup_file <- create_experiment(
name = "unique-outliers",
setup = "setup false",
go = NULL,
metrics = c(
'unique-outliers [1 2 3 4 100 100 500 500] 1.5',
'unique-outliers [1 2 3] 3'
)
)results <-
model_path |>
run_experiment(
setup_file = setup_file
)
#> ℹ Running model
#> ✔ Running model [5.4s]
#>
#> ℹ Gathering metadata
#> ✔ Gathering metadata [9ms]
#>
#> ℹ Processing table output
#> ✔ Processing table output [8ms]
#>
#> ℹ The experiment run produced the following messages:
#>
#> Picked up JAVA_TOOL_OPTIONS: -Dcom.sun.media.jai.disableMediaLib=truetest_that("unique-outliers", {
results |>
extract2("table") |>
pull(unique_outliers_1_2_3_4_100_100_500_500_1_5) |>
expect_equal("[100 500]")
results |>
extract2("table") |>
pull(unique_outliers_1_2_3_3) |>
expect_equal("[]")
})
#> Test passed with 2 successes 🥳.