mutation testing from a five year old (according to chatgpt)

I asked ChatGPT to explain mutation testing to a five year old to see what it would say. I got this back:

Alright, imagine you have a big puzzle made of Lego blocks. Each block represents a piece of code that makes up a computer program. Now, let’s say we want to make sure that our puzzle is put together correctly so that our program works properly.

Mutation testing is like having a mischievous Lego wizard who comes and changes some of the pieces in our puzzle to see if we notice. He might swap a blue block for a red one, or turn a block upside down. After he’s done, we carefully check our puzzle again to see if it still looks right and if our program still works.

If we can still solve the puzzle and our program still works even with the changes the wizard made, then we know our puzzle – or our program – is really strong. But if we can’t solve the puzzle or our program stops working, then we know there’s a problem and we need to fix it. So, mutation testing helps us make sure our programs are really robust and can handle unexpected changes.

It’s a good example of how ChatGPT is an aide and not a solver. First paragraph is good. In the second part, I love the mischievous Lego wizard who comes and changes some of the pieces to see if we notice. It’s a good premise. We see if our program still works with the changes.

The third paragraph leads us astray. If we notice the change, that is good. If the program stops working it is also good, means we noticed the change. Mutation testing is about having good tests that can detect change. Not a robust program with unnecessary code that behaves the same way when changed.

multiple ways of using soft asserts in junit 5

Do you think this test passes?

    @Test
    void mystery() {
        var softly = new SoftAssertions();
        softly.assertThat("robot").isEqualTo("izzy");
        softly.assertThat(126).isLessThanOrEqualTo(125);
    }

It does! Since softly is never told that it is done, the test does not fail. I’ve been using the approach of calling assertAll() to get my tests to fail:

    @Test
    void callingAssertAll() {
        var softly = new SoftAssertions();
        softly.assertThat("robot").isEqualTo("izzy");
        softly.assertThat(126).isLessThanOrEqualTo(125);
        softly.assertAll();
    }

I learned there are a few other approaches today. One is using an autocloseable version. This one is great if you are using a local variable:

    @Test
    void autoclosable() {
        try (var softly = new AutoCloseableSoftAssertions()) {
            softly.assertThat("robot").isEqualTo("izzy");
            softly.assertThat(126).isLessThanOrEqualTo(125);
        }
    }

Alternatively, you can use the lambda version. I like the autoclosable one better as it doesn’t encourage cramming stuff in a lambda. I could call another method inside assertSoftly with the actual asserts but that doesn’t seem better than using try with resources.

    @Test
    void lambda() {
        SoftAssertions.assertSoftly(s -> {
            s.assertThat("robot").isEqualTo("izzy");
            s.assertThat(126).isLessThanOrEqualTo(125);
        });
    }

There’s another way with an extension (that comes with asserj-core). I like this approach as it uses an instance variable and doesn’t require figuring out a place to call assertAll

@ExtendWith(SoftAssertionsExtension.class)
class SoftAssertionsExtensionTest {

    @InjectSoftAssertions
    SoftAssertions softly;

    @Test
    void field() {
        softly.assertThat("robot").isEqualTo("izzy");
        softly.assertThat(126).isLessThanOrEqualTo(125);
    }
}

The same can be done with a method. It’s nice to have choices!

@ExtendWith(SoftAssertionsExtension.class)
class SoftAssertionsExtensionTest {

    @Test
    void parameter(SoftAssertions softly) {
        softly.assertThat("robot").isEqualTo("izzy");
        softly.assertThat(126).isLessThanOrEqualTo(125);
    }

}