Skip to content

Reform tests according to Good Scientific Code practices #634

Open

Description

@Datseris

One of the things the Good Scientific Code Workshop teaches is writing good unit tests. I have to admit, Agents.jl suffers a lot from "bad tests", primarily because of its old age. When I got involved with the repo it was so many years ago I didn't have much bearings of good software development practices. While refreshing Agents.jl to v7 it became again apparent that the low quality test suite makes development harder. We should try to improve the test suite in the following ways:

1. Ensure independence

remove "global" definitions in main test file

The problem now is that the main runtest.jl file just defines a buuuuuuunch of stuff that are used in a bunch of other test files.
But tests should be atomic: each file should be fully independent and be runnable without relying on any other file being run before. For example, the file test/collect_tests.jl does a good job of that and successfully does not rely on any other file having been run first.

each file is a test suite

Something I have started doing in other packages of JuliaDynamics is to make each package be a test suite without explicitly defining an @testset statement. This is done with the wonderful function (see e.g., the tests of Attractors.jl).

using Test

defaultname(file) = uppercasefirst(replace(splitext(basename(file))[1], '_' => ' '))
testfile(file, testname = defaultname(file)) = @testset "$testname" begin
    include(file)
end

@testset "Attractors.jl" begin
    @testset "mapping" begin
        testfile("mapping/grouping.jl")
        testfile("mapping/recurrence.jl")
        # ...
    end

    @testset "basins analysis" begin
        testfile("basins/tipping_points_tests.jl")
        # ....
    end

    # ...
end

Which also means that test files should be grouped into folders for easier navigation!

2. Follow good test suite practices

I am pasting here the slide with "good advice on writing unit tests":

  • Actually unit: test atomic, self-contained functions. Each test must test only one thing, the unit. When a test fails, it should pinpoint the location of the problem. Testing entire processing pipelines (a.k.a. integration tests) should be done only after units are covered, and only if resources/time allow for it!
  • Known output / Deterministic: tests defined through minimal examples that their result is known analytically are the best tests you can have! If random number generation is necessary, either test valid output range, or use seed for RNG
  • Robust: Test that the expected outcome is met, not the implementation details. Test that the target functionality is met without utilizing knowledge about the internals. Also, never use internal functions in the test suite.
  • High coverage: the more functionality of the code is tested, the better
  • Fast: use the minimal amount of computations to test what is necessary
  • Regression: Whenever a bug is fixed, a test is added for this case
  • Input variety: attempt to cover a wide gambit of input types

3. Resolve specific issues with Agents.jl

Specific problems are:

  • They rely way too much on random numbers, where we should be writing analytic tests instead. Deterministic, analytically resolvable tests are the most accurate ones. The expected outcome of a test comes from pen and paper instead of running code. Right now we have a large amount of tests that were generated like "seed an rng, run some model code, write down the result as a test suite.". What we should be doing instead is "devise a distribution of agents. Analytically resolve their neighbors and actions given some rules and distances. Write tests that test again this analytically resolved output." I recall we had problems many times throught the lifespan of Agents.jl with random tests being broken etc. (obviously, random stuff like the RNG in the model should also be tested, but if you can avoid randomness in the tests, you should)
  • They are not computationally minimal, which means that you should be doing the least amount of computer operations to confirm whether some functionality works. E.g., look at the start of continuousSpace_tests.jl. 8 spaces are initialized, but as far as the subsequent tests are concerned, you only need 3. In a similar vein, the test aren't also memory minimal. For the majority of the tests, we use agents that have extra fields like weight, even though we don't actually use these fields anywhere. E.g., see "mutable graphs" tests, which never uses the weight field yet assigns it anyways.

4. Remove dependence on AgentsExampleZoo.jl

The tests should not depend on the package AgentsExampleZoo.jl as this both breaks the independence principle and introduces cyclic dependencies.


One doesn't have to worry about re-writing all tests. In fact, a PR "correcting" a single test file is already very much welcomed!

Note: issue description has been updated in 22-Feb-26.

Activity

  1. added
    help wantedHelp from someone external is needed
    simplificationReduce the amount of code, make it more clear and more simple, reduce the amount of functionalities
    and removed
    clarityRegardig clarity of source code (not docs)
    on Jul 1, 2022
  2. hbsmith commented on Aug 25, 2022

    @hbsmith
    Contributor

    @Datseris I'd be interested to know what was discussed at the workshop if you have more information on that

  3. Datseris commented on Aug 25, 2022

    @Datseris
    MemberAuthor
  4. Datseris commented on Aug 25, 2022

    @Datseris
    MemberAuthor

    here's a checklist with what properties good tests should have: JuliaDynamics/ChaosTools.jl#264

  5. Datseris commented on Oct 6, 2023

    @Datseris
    MemberAuthor

    Oh god, sample! tests are ridiculous. We need to re-write them. They shouldn't be more than 10 lines of code.

  6. pinned this issue on Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    good first issueGood for newcomers / easy to resolvehelp wantedHelp from someone external is neededsimplificationReduce the amount of code, make it more clear and more simple, reduce the amount of functionalitiestests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions