Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure unit testing for AnyLogic agent code?

How do you configure unit testing framework to help develop code that is part of AnyLogic agents?

To have a suitable test driven development rhythm, I need to be able to run all tests in a few seconds. I thought of exporting the project as a standalone application (jar) each time, but that's pretty slow.

I thought of trying to write all the code outside AnyLogic in separate classes, but there are many references to built-in AnyLogic classes, as well as various agents. My code would need to refer to these somehow, and I'm not sure how to do that except by writing the code inside AnyLogic.

I wonder if there's a way of adding the test runner as a dependency, and executing that test runner from within AnyLogic.

Does anyone have a setup that works nicely?

like image 253
John Avatar asked Jul 19 '26 15:07

John


2 Answers

This definitely requires some advanced Java, but testing, especially unit testing is too often neglected in building good robust models. I hope this simple example is enough to get you (and lots of other modellers) going.

For Junit testing, we make use of two libraries that you can add as a dependency to your model.

enter image description here

Now there are two main types of logic that you will want to test in simulation models.

  1. Functions in Java classes
  2. Model execution

Type 1: Suppose I have this very simple Java class

public class MyClass {

    public MyClass() {
    }
    
    public boolean getResult() {
        return true;
    }
}

And I want to test the function getResult()

I can simply create a new class and create a function that I annotate with the @Test modifier and then also make use of the assertEquals() method, which is standard in junit testing

import org.junit.Test;
import static org.junit.Assert.assertEquals;
    
public class MyTestClass{

    @Test
    public void testMyClassFunction1() {
        boolean result = new MyClass().getResult();
        assertEquals("The value of the test class 1", result, true);
    }

Now comes the AnyLogic specific implementation (there are other ways to do this but this is the easiest/most useful, you will see in a minute)

You need to create a custom experiment

enter image description here

Now if you run this from the Run Model button you will get this output

enter image description here

SUCCESS

Run: 1
Failed: 0


You can obviously update and change the output as to your liking

Type 2: Suppose we have this very simple model

enter image description here

And the function getResult() simply returns an int of 2.

Now we need to create another custom experiment to run this model

enter image description here

And then we can write a test to run this Custom Experiment and check the result

Simply add the following to your MyTestClass

@Test
    public void testMyClassFunction2() {
        int result = new SingleRun(null).runExperiment();
        assertEquals("Value of a single run", result, 2);
    }

And now if you run the RunAllTests customer experiment it will give you this output

SUCCESS

Run: 2
Failed: 0

This is just the beginning, you can read up tons on using junit to your advantage

like image 137
Jaco-Ben Vosloo Avatar answered Jul 24 '26 23:07

Jaco-Ben Vosloo


Short instructions for JUnit 5 and 6:

  • Create a fat jar with all the dependencies
// build.gradle.kts
plugins {
    id("java")
    id("com.gradleup.shadow") version "9.0.0-beta7"
}

val junitVersion = "5.11.4"

group = "com.zenmo.shadowjunit"
version = junitVersion
description = "Creates a fat jar of JUnit so it can be easily imported in AnyLogic"

repositories {
    mavenCentral()
}

dependencies {
    implementation(platform("org.junit:junit-bom:$junitVersion"))
    implementation("org.junit.jupiter:junit-jupiter")
    implementation("org.junit.platform:junit-platform-launcher")
}

tasks.shadowJar {
    archiveBaseName = "junit-shadow"
    archiveClassifier = ""
    archiveVersion = junitVersion
}
  • Run gradle shadowJar
  • include the artifact from build/libs/junit-shadow-5.11.4.jar in your AnyLogic project. You can also create a second project just for testing your main project so JUnit and test code is excluded from the final executable.
  • Write tests as a Java classes
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class class MyAnyLogicTest {
    @Test
    void testSomething() {
        // instantiate and agents here and call methods
        assertEquals(2, 1 + 1);
    }
}
  • Write startup code. You can chose where to put this. For example:
    • in the main agent
    • in an experiment
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
    .selectors(
        selectPackage("com.zenmo.zeroloadertest")
        //selectClass(FirstTest.class)
    )
    .filters(
        includeClassNamePatterns(".*Test")
    )
    .build();
    
SummaryGeneratingListener listener = new SummaryGeneratingListener();

try (LauncherSession session = LauncherFactory.openSession()) {
    var launcher = session.getLauncher();
    launcher.registerTestExecutionListeners(listener);
    launcher.execute(request);
}

listener.getSummary().printTo(new PrintWriter(System.out));
listener.getSummary().printFailuresTo(new PrintWriter(System.err));

imports needed to make this work:

import static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;

import java.io.PrintWriter;

import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryListener;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.LauncherSession;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
like image 21
Erik van Velzen Avatar answered Jul 25 '26 00:07

Erik van Velzen