0

I am writing an application to draw sketches in javaFX.

Currently you can draw and erase with different colors. Among the commands I have written is "erase all". But I don't know how to test it. This command allows you to erase all the lines drawn on the canvas. There can be many approaches to achieve this. For example, you could replace the entire canvas with a new one to "reset" it, or you could use several functions to act on the color and return all pixels to the original color of the canvas, for example. I chose the second approach. So, the command I bind to the button is as follows:

import javafx.scene.canvas.Canvas;

public class EraseAllCommand implements Command {
    private final Canvas canvas;

    public EraseAllCommand(Canvas canvas) {
        this.canvas = canvas;
    }

    @Override
    public void execute() {
        this.canvas
                .getGraphicsContext2D()
                .clearRect(0, 0, this.canvas.getWidth(), this.canvas.getHeight());
    }
}

Now, what is the outcome to consider for testing this behavior effectively?

  • Class javafx.scene.canvas.GraphicsContext is a final class and does not extends or implements any class or interface, so I didn't know how to fake it. I considered to use mocks of Mockito, then.
  • I found this question I considered useful but in my case I get a "blank" canvas without any css property modification (see my implementation above).
  • I could use robots but I don't think is a good solution. Should I check the color of every pixel? Is there a performant way to achieve this?

I finally created a mock but is strongly based on implementation and that's bad because there are several approaches to solve this problem. Could I mock in some cleverer ways? I created a stub Canvas of mine just to wrap a mock GraphicsContext into it. What should I consider as a testable outcome?

Here is my bad test:

public class EraseAllTest {
    @Test
    public void should_erase_all() {
        GraphicsContext mockGraphicsContext = Mockito.mock( GraphicsContext.class );
        final var eraseAllCommand = new EraseAllCommand( new StubCanvas( mockGraphicsContext ));
        eraseAllCommand.execute();
        Mockito.verify( mockGraphicsContext ).clearRect(0, 0, 0, 0);
    }
}

I am currently using javaFX 18, junit5 5.9.0, Mockito 4.0.0.

A perfect solution, to me, would be an approach let me change the implementation without change the test. Any suggestions? Thanks a lot.

TimT
  • 31
  • 4
  • Can you scan a `snapshot` of the `Canvas`? Some examples are cited [here](https://stackoverflow.com/a/45685372/230513). – trashgod Sep 30 '22 at 12:31
  • Can you even do things like this in JUnit? Is anything meaningful going to happen off the FXAT? – DaveB Sep 30 '22 at 14:23
  • It seems like you are trying to test that `GraphicsContext2D.clearRect(...)` is doing what it is expected to do, rather than testing if the code you have written is doing what it is expected to do. I'm not convinced that's the job of your unit tests. – James_D Sep 30 '22 at 15:56
  • I agree here. Your CUT (Class under Test) has zero logic and zero testable code since it delegates 100% of its actions to Canvas. In fact, it looks like it has an overly-complicated structure since the Canvas is passed into the constructor mostly likely for the sole purpose of passing in a Mock during testing. But there's nothing to test here. – DaveB Sep 30 '22 at 16:31
  • In fact, the only thing that could go wrong with this CUT is if canvas.getWidth() or getHeight() somehow don't return the correct values due to some quirk of the rendering engine. But that seems impossible to test without the rendering, which isn't going to happen with simple JUnit tests. – DaveB Sep 30 '22 at 16:33
  • The idea here was to make sure that the ```GraphicsContext2D.clearRect(...)``` function was actually called, not that it does what it's supposed to do. Being certain that a function is called is often the reason why stubs are injected for testing, right? – TimT Sep 30 '22 at 17:07

0 Answers0