Write tests
Tests should be written for every workflow/workflowstep, as well as every important class. They are written in Java, with JUnit 5.
In order to create a new test, the easiest way is to right click on the class to be tested, in Eclipse, and choose New > JUnit Test Case
.
Basic example
- The first step is to write just enough code so that the tests compile:
package eu.simstadt.workflows; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class VeryBasicTests { @Test void testStringConcatenation() { assertEquals("ab", f("a", "b"), "f should concatenate strings."); } private String f(String a, String b) { return null; } }
- Tests can be run with
Run As > JUnit Test
. The tests should fail. This step is really important, because otherwise, it's all too easy to write tests which don't test anything:org.opentest4j.AssertionFailedError: f should concatenate strings. ==> expected: <ab> but was: <null> at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1135) at eu.simstadt.workflows.VeryBasicTests.testStringConcatenation(VeryBasicTests.java:10) ...
- Without touching the testing methods, the tested code should be modified until the tests pass:
private String f(String a, String b) { - return null; + return a + b; }
- The tests should be run every time the corresponding code is modified. Continuous integration software, such as Jenkins, can help.
Complete example
Here is a complete example for a photovoltaic analysis in La Réunion.
It creates a workflow, runs it and checks if the results (temperature, irradiance, orientation on flat roof) are plausible for a location in the Southern Hemisphere.
package de.hftstuttgart.simstadtworkflows.casestudies;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import de.hftstuttgart.hierarchicworkflow.Project;
import de.hftstuttgart.hierarchicworkflow.WorkflowUtils;
import de.hftstuttgart.simstadtworkflows.energy.PhotovoltaicPotentialAnalysisWorkflowProvider;
import eu.simstadt.datamodel.SimStadtModel;
import eu.simstadt.irradiance.IrradianceTable;
import eu.simstadt.utils.TestsUtils;
import eu.simstadt.utils.UTF8WithBOM;
import eu.simstadt.weather.data.WeatherDataSet;
import eu.simstadt.workflows.CityGmlWorkflow;
import eu.simstadt.workflowsteps.IrradianceProcessor;
import eu.simstadt.workflowsteps.PhotovoltaicPotential;
import eu.simstadt.workflowsteps.WeatherProcessor;
class LaReunionPhotovoltaicPotentialTests
{
@Test
void testPhotovoltaicAndWeatherInSouthernHemisphere() throws Exception {
String cityGMLBaseName = "Bldgtrial_3DLoD2_LaReunion_SteMarie_LaConvenance";
Project repo = TestsUtils.testRepository();
Project project = repo.projectNamed("LaReunion").get();
CityGmlWorkflow<SimStadtModel> workflow = new PhotovoltaicPotentialAnalysisWorkflowProvider()
.createWorkflow("PV Potential tests in LaReunion");
try {
project.addWorkflow(workflow);
workflow.addCityGmlFileName(cityGMLBaseName + ".gml");
PhotovoltaicPotential pvStep = TestsUtils.getWorkflowstep(workflow,
PhotovoltaicPotential.class);
workflow.createDirectoriesAndSaveWorkflow();
Path yearlyCSVPath = pvStep.getLocation().resolve(cityGMLBaseName +
"_inseldb_Hay_pv_potential.csv");
SimStadtModel model = workflow.initAndRun().get(0);
WeatherDataSet weather = model.getAttribute(WeatherProcessor.WEATHER_DATA_SET);
double[] monthlyTemperatures = weather.getAmbientTemperature().getMonthlyAverages();
double[] monthlyIrradiances = weather.getGlobalHorizontalIrradiance().getMonthlyAverages();
IrradianceTable irradianceTable = model.getAttribute(IrradianceProcessor.IRRADIANCE_TABLE);
assertTrue(
irradianceTable.getAverageIrradiance(0, 30) >
irradianceTable.getAverageIrradiance(180, 30) * 1.20,
"Modules to the North should get much more irradiance than to the South");
assertTrue(monthlyIrradiances[0] > monthlyIrradiances[5] * 1.20,
"January should be summer");
assertTrue(monthlyTemperatures[0] > monthlyTemperatures[5] + 5,
"January should be summer");
String yearlyCSVContent = UTF8WithBOM.read(yearlyCSVPath);
assertTrue(yearlyCSVContent.contains("Module azimuth on flat roof;0;°"),
"Modules should be oriented towards the north, in the southern hemisphere.");
} finally {
WorkflowUtils.deleteDirectory(workflow.getLocation());
}
}
}