1

I need help with a little experiment. I have two test files:

testFileA.java and testFileB.java

testFileA.java looks like

    @Test
    public void test1() throws Throwable {
        Instant start = Instant.now();
        mvc.perform(MockMvcRequestBuilders
                        .post('someEndpoint/')
                        .andDo(print())
                        .andExpect(status().isCreated())
        Instant end = Instant.now();
        System.out.println(">>>>>>>>>>>>>>>>>End" + Duration.between(start, end));
    }

    @Test
    public void test2() throws Throwable {
        //same contents as test 1
    }

    @Test
    public void test3() throws Throwable {
        //same contents as test 1
    }

testFileB.java looks like

    @Test
    public void test1() throws Throwable {
        Instant start = Instant.now();
        this.abstractedCode();
        Instant end = Instant.now();
        System.out.println(">>>>>>>>>>>>>>>>>End" + Duration.between(start, end));
    }

    @Test
    public void test2() throws Throwable {
           //same contents as test 1
    }

    @Test
    public void test3() throws Throwable {
        //same contents as test 1
    }

    public void abstractedCode(){
      mvc.perform(MockMvcRequestBuilders
                        .post('someEndpoint/')
                        .andDo(print())
                        .andExpect(status().isCreated())
    }

My expectation with testFileB.java is that the first test would take a little longer and then each subsequent test wouldn't take as long because the duplicated code is refactored into a method call; the Java JIT compiler makes it so we wouldn't have to recompile the same code again. My expectation was proven correct.

My expectation with testFileA.java is that each test would take as long as the previous one because the common code wasn't refactored into a function, but that ended up not being true. The behavior was the same, the first test took a little longer and then the next two were shorter.

I'm guessing that something is happening within the JVM that makes it so the mvc.perform() method that I'm calling doesn't have to get recompiled over and over again, the JIT compiler is helping my program out despite the fact that it's not in a function I've defined. Is that true?

Will
  • 71
  • 8
  • 1
    You seem to have the general misconceptions about JIT and performance measurements. First of all: the JIT kicks only in after THOUSANDS of usages. Beyond that, measuring is *hard*. See https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java for starters. – GhostCat Aug 19 '20 at 14:31
  • Noted, so I'm not doing benchmarking correctly and I can probably attribute the decrease in time I'm seeing with my tests as a fluke? – Will Aug 19 '20 at 14:35
  • It is very hard to say without any insight what exactly you are running, and what your results. are. See my answer: I think it is really not worth (much) following up on this. – GhostCat Aug 19 '20 at 14:38

1 Answers1

2

A distinct non-answer: you are investing your time in the wrong place.

The gains from "JIT optimisations" only matter in a setup where an application runs for lengthy periods of time, invoking the same method millions of times.

The point of a JIT is NOT to optimize test methods that are invoked ONCE during the lifetime of a JVM.

Meaning: invest your time to train how to write testable production code, and clean test code. That is much more important here.

Of course: the execution time of your unit test suite still matters. But when that turns into an issue, then you are most likely facing poorly written tests, that for example use default timeouts and whatnot ... ending up with a test taking many seconds, if not minutes. That is something you care about. You absolutely do not care about JIT magic though for unit test environments.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • The whole point of my question was to convince my coworkers to write cleaner tests, haha. I thought that if I could show them that there were measurable perfomance gains from turning common code into methods then it may convince them to write cleaner tests, not the 3000 line test files/duplicate code horror that currently exists in our tests as well as our codebase. Back to the drawing board I guess, I appreciate your answer. – Will Aug 19 '20 at 14:41
  • 2
    Then use the "correct!" argument. Explain to them: what matters is that your test run "fast enough", but also: that they can easily be read, understood and enhanced. They should help you move faster, and fix bugs quickly. Focus on that. Performance in the milli seconds range is NOT your primary concern here. – GhostCat Aug 19 '20 at 14:43
  • 1
    @Will: Focus on measurable *productivity* gains rather than the length of time taken to run your tests. You should almost certainly ignore "time taken to run the tests" and focus on readability. – Jon Skeet Aug 19 '20 at 14:43
  • 1
    @JonSkeet Yeah, well. A lot of people say that ... but every time you are sloppy, and a test needs 1 second more than it ought to ... when 50 people do that once a month, all of a sudden, your unit test suite needs 10 minutes more after a year. The turn-around-time for the whole testbase is something that often gets ignored ... unless it turns out to be painful. But you get there with many many small steps. – GhostCat Aug 19 '20 at 14:46
  • 1
    @GhostCat: Yes, it's good to keep test time down - but *after* you've already got clean tests. What I guess I meant is **for now** (Will) should almost certainly ignore "time taken to run tests". – Jon Skeet Aug 19 '20 at 14:47
  • On a side note, do you have any recommendations on materials where I can learn about the inner workings of the JVM? I'd love to learn more. – Will Aug 19 '20 at 15:06