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.
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.
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.
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.