diff --git a/src/core/output.jl b/src/core/output.jl
index 8bf8dcce92e97ab1436044fd0d35846bff9daa79..b6437ec42c02190c8c7dd9439f5a2cc2b67cbe47 100644
--- a/src/core/output.jl
+++ b/src/core/output.jl
@@ -166,7 +166,8 @@ Cycle through all registered data outputs and activate them according to their
 configured frequency.
 """
 function outputdata(model::AgentBasedModel)
-    #XXX all output functions are run on the first update (regardless of frequency)
+    #TODO enable output every X days
+    #XXX all output functions except for "end" are run on the first update
     # -> should they all be run on the last update, too?
     startdate = @param(core.startdate)
     isnextmonth = d -> (day(d) == day(startdate))
diff --git a/test/io_tests.jl b/test/io_tests.jl
index 2180c599d5018eec8dc290c876f3251270ef350a..2fb71c9f13ef9bcf127680216c362b3efd03eefd 100644
--- a/test/io_tests.jl
+++ b/test/io_tests.jl
@@ -18,10 +18,7 @@
 end
 
 @testset "Output functions" begin
-    properties = Dict{Symbol,Any}(:settings=>TESTSETTINGS,
-                                  :dataoutputs=>Vector{DataOutput}())
-    space = GridSpace((10,10), periodic=false)
-    model = AgentBasedModel(Animal, space, properties=properties, warn=false)
+    model = smalltestlandscape()
     # test that the output directory is created with all files
     outdir = @param(core.outdir)
     Ps.createdatadir(outdir, @param(core.overwrite))
@@ -35,9 +32,29 @@ end
     @test isfile(joinpath(outdir, @param(core.configfile)))
     @test isfile(joinpath(outdir, Ps.LOGFILE))
     # check whether the overwrite warning/protection works
-    logstring = "Overwriting existing output directory $(outdir)."
-    #TODO test overwrite protection
-    @test_logs (:warn, logstring) match_mode=:any Ps.createdatadir(outdir, @param(core.overwrite))
+    @test_logs((:warn, "Overwriting existing output directory $(outdir)."),
+               match_mode=:any,
+               Ps.createdatadir(outdir, @param(core.overwrite)))
+    @test_throws ErrorException Ps.createdatadir(outdir, false)
+    # test that creating a DataOutput works, and outputs data with the required frequency
+    testoutput(model) = string(model.date)*"\n"
+    Ps.newdataoutput!(model, "never.csv", "Date", testoutput, "never")
+    Ps.newdataoutput!(model, "daily.csv", "Date", testoutput, "daily")
+    Ps.newdataoutput!(model, "monthly.csv", "Date", testoutput, "monthly")
+    Ps.newdataoutput!(model, "yearly.csv", "Date", testoutput, "yearly")
+    Ps.newdataoutput!(model, "end.csv", "Date", testoutput, "end")
+    @param(core.enddate) = Date(2023,2,1)
+    @test_logs((:info, "Simulated 366 days."),
+               match_mode=:any,
+               simulate!(Ps.withtestlogger(model)))
+    @test !isfile(joinpath(outdir, "never.csv"))
+    @test isfile(joinpath(outdir, "daily.csv"))
+    @test countlines(joinpath(outdir, "daily.csv")) == 367
+    @test isfile(joinpath(outdir, "monthly.csv"))
+    @test countlines(joinpath(outdir, "monthly.csv")) == 14
+    @test isfile(joinpath(outdir, "yearly.csv"))
+    @test countlines(joinpath(outdir, "yearly.csv")) == 3
+    @test isfile(joinpath(outdir, "end.csv"))
+    @test countlines(joinpath(outdir, "end.csv")) == 2
     rm(outdir, force=true, recursive=true)
-    #TODO test that creating a DataOutput works, and outputs data with the required frequency
 end
diff --git a/test/landscape_tests.jl b/test/landscape_tests.jl
index 7e338e9469a4641cf40d2eaa8590181f74ca83f3..e989fdb41eea1ad201b1f2a96cf9965fdd06e5e0 100644
--- a/test/landscape_tests.jl
+++ b/test/landscape_tests.jl
@@ -3,38 +3,6 @@
 ### These are the tests for the core landscape functions.
 ###
 
-"""
-    smalltestlandscape()
-
-Create a 6x6 landscape with three land cover types for testing:
-
-    F F F F F F
-    F F F F F F
-    F F F F F F
-    F F F F F W
-    F F G G G G
-    F F G G G G
-"""
-function smalltestlandscape()
-    landscape = Matrix{Pixel}(undef, 6, 6)
-    for x in 1:6
-        for y in 1:6
-            (x in (1:2) || y in (1:4)) ? lc = Ps.forest : lc = Ps.grass
-            landscape[x,y] = Pixel(lc, missing, [])
-        end
-    end
-    landscape[6,4] = Pixel(Ps.water, 0, [])
-    space = GridSpace(size(landscape), periodic=false)
-    properties = Dict{Symbol,Any}(:date=>TESTSETTINGS["core.startdate"],
-                                  :landscape=>landscape,
-                                  :events=>Vector{FarmEvent}(),
-                                  :logger=>global_logger(),
-                                  :dataoutputs=>Vector{DataOutput}(),
-                                  :settings=>TESTSETTINGS)
-    return AgentBasedModel(Union{Farmer,Animal,FarmPlot}, space, properties=properties,
-                           rng=StableRNG(TESTSETTINGS["core.seed"]), warn=false)
-end
-
 @testset "Landscape initialisation" begin
     # initialise the landscape part of the model
     landscape = Ps.initlandscape(TESTSETTINGS["core.landcovermap"],
diff --git a/test/paramscan.toml b/test/paramscan.toml
index cd3f513d8499f869b43bdd104e6a83073992502d..ec44c3e14e7b53c6640e6882d8721591a3c54be1 100644
--- a/test/paramscan.toml
+++ b/test/paramscan.toml
@@ -3,6 +3,8 @@
 ### This configuration file is intended to test the parameter scanning feature.
 ###
 
+#XXX remember that changes here may break tests!
+	
 [core]
 configfile = "test/paramscan.toml" # location of the configuration file
 landcovermap = "landcover_jena.tif" # location of the landcover map
diff --git a/test/runtests.jl b/test/runtests.jl
index 1542cc2dcbb68c77b7ef78a64a4ac4e7d8aa4a2c..24aea56a16c6c8c40e7cadb762f8dae290a823ba 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -20,6 +20,38 @@ const Ps = Persephone
 const TESTPARAMETERS = joinpath(pkgdir(Persephone), "test/test_parameters.toml")
 const TESTSETTINGS = Ps.getsettings(TESTPARAMETERS)
 
+"""
+    smalltestlandscape()
+
+Create a 6x6 landscape with three land cover types for testing:
+
+    F F F F F F
+    F F F F F F
+    F F F F F F
+    F F F F F W
+    F F G G G G
+    F F G G G G
+"""
+function smalltestlandscape()
+    landscape = Matrix{Pixel}(undef, 6, 6)
+    for x in 1:6
+        for y in 1:6
+            (x in (1:2) || y in (1:4)) ? lc = Ps.forest : lc = Ps.grass
+            landscape[x,y] = Pixel(lc, missing, [])
+        end
+    end
+    landscape[6,4] = Pixel(Ps.water, 0, [])
+    space = GridSpace(size(landscape), periodic=false)
+    properties = Dict{Symbol,Any}(:date=>TESTSETTINGS["core.startdate"],
+                                  :landscape=>landscape,
+                                  :events=>Vector{FarmEvent}(),
+                                  :logger=>global_logger(),
+                                  :dataoutputs=>Vector{DataOutput}(),
+                                  :settings=>TESTSETTINGS)
+    return AgentBasedModel(Union{Farmer,Animal,FarmPlot}, space, properties=properties,
+                           rng=StableRNG(TESTSETTINGS["core.seed"]), warn=false)
+end
+
 @testset "Persephone Tests" begin
     @testset "Core model" begin
         include("io_tests.jl")
@@ -40,5 +72,5 @@ end
 
 # NOTE: Due to an issue with Julia (https://github.com/JuliaLang/julia/issues/48456),
 # whenever we are using `@test_logs` with a function that takes a model object, we have
-# to wrap that model object in `withtestlogger()`. (For an example, see the "Model
+# to wrap that model object in `Ps.withtestlogger()`. (For an example, see the "Model
 # simulation" testset in simulation_tests.jl.)
diff --git a/test/simulation_tests.jl b/test/simulation_tests.jl
index 7701033638bbe8fc870ea2c9560dfb0e90ef485e..678bf3f86a3064c434b3438305deb7b60754dd28 100644
--- a/test/simulation_tests.jl
+++ b/test/simulation_tests.jl
@@ -17,22 +17,25 @@ end
 
 @testset "Parameter scanning" begin
     config = "paramscan.toml"
-    testdirs = ["results_test_paramscan/seed_1_loglevel_debug_overwrite_ask",
-                "results_test_paramscan/seed_1_loglevel_debug_overwrite_true",
+    testdirs = ["results_test_paramscan/seed_1_loglevel_warn_overwrite_ask",
+                "results_test_paramscan/seed_1_loglevel_warn_overwrite_true",
                 "results_test_paramscan/seed_2_loglevel_info_overwrite_true"]
     settings = Ps.getsettings(config)
     scanparams = settings["internal.scanparams"]
     @test length(scanparams) == 3
     @test sort(["core.overwrite", "core.loglevel", "core.seed"]) == sort(scanparams)
     scan = Ps.paramscan(settings, scanparams)
-    outdirs = (s["core.outdir"] for s in scan)
+    outdirs = map(s -> s["core.outdir"], scan)
     @test length(outdirs) == 12
-    @test length(initialise(config)) == 12 #XXX This takes a long time
+    #TODO while parallelisation doesn't work yet, initialising 12 models takes too
+    # long to do every time the test suite is run. Therefore, I'm commenting out
+    # some tests here, to be reinstated once parallelisation is implemented.
+    #@test length(initialise(config)) == 12
     for dir in testdirs
         @test dir in outdirs
-        @test isdir(dir)
+        #@test isdir(dir)
     end
-    rm("results_test_paramscan", force=true, recursive=true)
+    #rm("results_test_paramscan", force=true, recursive=true)
 end
 
 @testset "Model simulation" begin
diff --git a/test/test_parameters.toml b/test/test_parameters.toml
index 7c8891dbac73c8bc10726165771bdfb07fdc46c4..45f37a310536899ca646a93ca61e0761eacc7609 100644
--- a/test/test_parameters.toml
+++ b/test/test_parameters.toml
@@ -3,6 +3,8 @@
 ### This configuration file is used for the test suite.
 ###
 
+#XXX remember that changes here may break tests!
+	
 [core]
 configfile = "test_parameters.toml" # location of the configuration file
 landcovermap = "landcover_jena.tif" # location of the landcover map