Trouble Unit Testing Commands in Java

Hey all, I’m trying to do some unit testing with the command-based framework, and I’m finding that I can test subsystems just fine, but commands don’t really want to work. Consider this subsystem:

public class ExampleSystem extends SubsystemBase {
    private CANSparkMax spark;

    public ExampleSubsystem() {
        spark = new CANSparkMax(0, MotorType.kBrushless);
    }

    public void run() {
        spark.set(0.3);
    }

    public void close() {
        spark.close();
    }

    public double getSpeedPercentage() {
        return spark.get();
    }
}

And this command:

public class RunCommand extends CommandBase {
    private ExampleSubsystem subsystem;

    public RunCommand(ExampleSubsystem subsystem) {
        this.subsystem = subsystem;
        addRequirements(subsystem);
    }

    @Override
    public void execute() {
        subsystem.run();
    }
}

Simple enough. However, the tests don’t seem to think so:

public class SubsystemTests {
    private static double delta = 1e-3; // 0.01
    ExampleSubsystem subsystem;

    @Before
    public void setup() {
        subsystem = new ExampleSubsystem();
    }

    @After
    public void shutdown() {
        subsystem.close();

    @Test // This test PASSES
    public void testSubsystem() {
        subsystem.run();
        assertEquals(0.3, subsystem.getSpeedPercentage(), delta);
    }

    @Test // This test FAILS
    public void testCommand() {
        RunCommand command = new RunCommand(subsystem);
        CommandScheduler scheduler = CommandScheduler.getInstance();

        scheduler.schedule(command);
        scheduler.run();

        assertEquals(0.3, subsystem.getSpeedPercentage(), delta);
    }
}

As I mention in the comments, the subsystem test passes while the command test fails. Am I doing something wrong with the CommandScheduler?

Are you certain your test is importing the correct RunCommand? WPILIB includes its own RunCommand class. I suspect your test is importing the WPILIB version instead of the one you have written.

Looking at my imports, I am importing the RunCommand from my own code and not WPIlib’s.

Okay. I’m looking through the CommandScheduler implementation. I wonder if you might be hitting this case .

Can you try enabling the CommandScheduler using the enable() method? For example:

    @Test
    public void testCommand() {
        RunCommand command = new RunCommand(subsystem);
        CommandScheduler scheduler = CommandScheduler.getInstance();
        scheduler.enable(); // Add this line.

        scheduler.schedule(command);
        scheduler.run();

        assertEquals(0.3, subsystem.getSpeedPercentage(), delta);
    }

If that still doesn’t work. Let’s see if we can try to find a way to verify the command is in fact being scheduled.

I was just looking through the API and I just noticed the enable method myself. I tried it as you said, but it made no difference :confused:

Something I tried was printing the result of scheduler.isScheduled() on the command, which resulted in false regardless of where I put it:

    @Test
    public void testCommand() {
        RunCommand command = new RunCommand(subsystem);
        CommandScheduler scheduler = CommandScheduler.getInstance();
        scheduler.enable();

        scheduler.schedule(command);
        System.out.println(scheduler.isScheduled(command)); // >false
        scheduler.run();
        System.out.println(scheduler.isScheduled(command)); // >false

        assertEquals(0.3, subsystem.getSpeedPercentage(), delta);
    }

I’m not entirely sure what’s preventing it from going into the scheduler. If there’s some runtime error happening, it’s not being printed to the console and running ./gradlew test --info or ./gradlew test --stacktrace isn’t revealing it.

Just on principal of wanting to figure out why it doesn’t work, I’ll keep digging. However, I want to call out that you can do this without the CommandScheduler at all:

    @Test
    public void testCommand() {
        RunCommand command = new RunCommand(subsystem);
        command.execute()
        assertEquals(0.3, subsystem.getSpeedPercentage(), delta);
    }

I think this is actually more in the spirit of a unit test. Like I said though, I’ll keep searching for a bit.

Oh lol, can’t believe I never thought of that :joy::joy:

If it is possible to use the scheduler for unit tests that would be nice, although I suppose it would be easier to simply use this method. Cheers.

Have you read the Unit Testing page in the documentation? There might be something there that you missed.


I’m probably going to be writing some sim / unit tests myself tomorrow, so if I find a fix for this then I’ll let you know.

I read it like 5 times. It doesn’t cover testing commands unfortunately. I saw this tutorial from 2019 that I wasn’t able to replicate unfortunately, although I’m not using Mockito here unlike they were.

There’s multiple conditions in that statement that all need to be false for it to be scheduled. Since a DS isn’t running, the commands runsWhenDisabled also needs to be true.

1 Like

Command-based was completely rewritten in 2020, that tutorial is completely obsolete. With the sim stuff added in 2020, there’s no reason at all to use Mockito.

As for the article, it was written on purpose to apply to both command-based and non-command-based teams.

You can use DriverStationSim.setEnabled(true) and some other methods to set the DS state. Similar classes for simulating joystick inputs exist as well.

2 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.