CanlabCore unit tests
May 7, 2026 · View on GitHub
This is the unit-test suite for CanlabCore. It uses MATLAB's built-in
matlab.unittest framework. There is no external runner — you can run any
single test by runtests('canlab_test_constructor'), or the whole suite
with canlab_run_all_tests.
Test files (and the runner and shared helpers) are namespaced with a
canlab_ prefix to avoid colliding with similarly-named files from other
toolboxes a user may have on their MATLAB path.
Running locally
cd CanlabCore/Unit_tests
results = canlab_run_all_tests; % unit suite (default)
results = canlab_run_all_tests('Tag', 'Core'); % only tagged 'Core'
results = canlab_run_all_tests('JUnit', 'results.xml'); % also write JUnit XML
results = canlab_run_all_tests('Walkthroughs', 'only'); % run only walkthrough tests
results = canlab_run_all_tests('Walkthroughs', 'include'); % unit + walkthrough
canlab_run_all_tests adds the parent CanlabCore/ tree to the MATLAB
path with subfolders, so you do not need to set up paths first. SPM and
Neuroimaging_Pattern_Masks must already be on your path if a test
requires them; tests that need them call assumeNotEmpty(which('...'))
and skip gracefully when absent.
Layout
Unit_tests/
├── canlab_run_all_tests.m entry point used locally and in CI
├── helpers/ shared fixtures
│ └── canlab_get_sample_fmri_data.m
├── fmri_data/ tests of the @fmri_data class
├── image_vector/ tests of @image_vector behavior shared by subclasses
├── statistic_image/ tests of @statistic_image (ttest, threshold)
├── region/ tests of @region construction and methods
├── atlas/ tests of @atlas
├── workflows/ end-to-end pipeline smoke tests
├── walkthroughs/ Tier B integration tests (canlab_help_* scripts)
└── old_to_integrate/ legacy ad-hoc scripts pending rewrite as proper tests
old_to_integrate/ is excluded from discovery by canlab_run_all_tests.
Files there should be ported into one of the other folders as proper
matlab.unittest tests (and renamed to canlab_test_*.m) over time.
walkthroughs/ contains thin wrappers around the canlab_help_*
walkthrough scripts that live in the
CANlab_help_examples
repository. Each wrapper provides a WorkingFolderFixture (clean cwd)
and close all force setup, then evals the corresponding help script
under evalc. Reaching the end of the script counts as a pass; we don't
verify pixel content or numerical results. These are slower and depend
on CANlab_help_examples being on the MATLAB path, so they are
skipped by default and run on a nightly schedule via
.github/workflows/tests-walkthroughs.yml. Pass 'Walkthroughs', 'only'
or 'Walkthroughs', 'include' to canlab_run_all_tests to opt in.
Writing a new test
Function-based tests are the default style. A test file is a function
named canlab_test_<thing>.m that declares its sub-tests:
function tests = canlab_test_thing
tests = functiontests(localfunctions);
end
function test_some_specific_behavior(tc)
obj = canlab_get_sample_fmri_data();
result = method_under_test(obj);
tc.verifyEqual(size(result.dat), size(obj.dat));
end
Conventions:
- One file per method or invariant. File name
canlab_test_<thing>.m, outer function name matching the file. Inner sub-test function names follow the matlab.unittest conventiontest_<specific_behavior>— they are local to the file and don't need thecanlab_prefix. - Use
tc.verify*(records failure, keeps going) for normal assertions. Reservetc.assert*for setup preconditions where the rest of the test cannot run. - Use
tc.assumeNotEmpty(which('foo.nii'))when a test requires a file that may not be on every contributor's path. The test will be marked Incomplete rather than failed. - Prefer
canlab_get_sample_fmri_data()(inhelpers/) over re-loading the sample directly so the sample-load path is centralized. - A test should not write outside a temp directory. If you need to write,
use
tempnameand clean up withtc.addTeardown(@delete, ...). - Tests should not depend on each other or on execution order.
Why canlab_test_* files instead of test_*?
matlab.unittest's TestSuite.fromFolder only auto-discovers files
whose names start or end with test (case-insensitive), so the runner
glob-discovers canlab_test_*.m and uses TestSuite.fromFile directly.
The result is the same; the difference is that canlab_test_constructor
on the path won't shadow some other toolbox's test_constructor.
Tags
Apply tags via class-based tests when a group of tests needs to be
selectable as a unit (e.g., 'RequiresMasks', 'Slow', 'GUI').
Tags filter via canlab_run_all_tests('Tag', 'Slow') or
canlab_run_all_tests('Tag', '~GUI').
CI
Every push and PR runs the suite on the latest supported MATLAB release
on Ubuntu. SPM and Neuroimaging_Pattern_Masks are checked out as
siblings. See .github/workflows/test.yml for the full configuration.