18

I'm trying to write a unit test that requires mulitple threads. However, it seems that the threads just stop part way through execution. Consider the following code:

public class Test {
    @org.junit.Test
    public void TestThreads() {
        new Thread(new Runnable() {
            public void run() {
                for (int i = 1; i < 1000; i++) System.out.println(i);
            }
        }).start();
    }
}

If I run this unit test, it will generally stop displaying output somewhere between 140-180. If I convert this code into a regular class and run it, it works fine. Does anybody have any idea what I'm missing here?

Thanks, - Andrew.

Andrew Clarke
  • 181
  • 1
  • 6

3 Answers3

28

You can use Thread.join() to prevent the test from finishing before the new thread has completed its task:

@org.junit.Test
public void TestThreads() throws InterruptedException {
    Thread t = new Thread(new Runnable() {
        public void run() {
            for (int i = 1; i < 1000; i++) System.out.println(i);
        }
    });
    t.start();
    t.join();
}

Normally, the JVM will terminate when the last non-daemon thread terminates. You might expect that simply calling t.setDaemon(false) on the thread would prevent the JVM from exiting before the task is finished. However, junit will call System.exit() when the main thread has finished.

As Gus notes in the comments: "you need join() because start() doesn't block".

He's correct that you could also call run() in this minimal example. However, I assume you're starting a thread because you want it to run concurrently with another thread. Calling run() on a Thread is flagged up as a possible mistake by FindBugs - if you're just going to call run(), you'd probably just want to implement Runnable instead of using Thread.

assylias
  • 321,522
  • 82
  • 660
  • 783
Martin Ellis
  • 9,603
  • 42
  • 53
  • Sigh - beat me to the punch fair and square. – Bob Cross May 17 '13 at 19:46
  • 1
    It's worth mentioning in your solution that you need Join() because Start() doesn't block. IIRC, You could also just use Run() if you're not going to do anything simulataneously. – Gus May 17 '13 at 19:49
  • @Gus using `run` on a `Thread` instance is a bad idea. Why? Because using `run` doesn't make it a thread, just a plain object. – Luiggi Mendoza May 17 '13 at 19:56
  • Thanks, this was exactly the problem. run() worked fine, but negated the multithreading which I needed for the test. You can maybe tell this is my first time writing a multithreaded Java program. – Andrew Clarke May 17 '13 at 19:59
  • @AndrewClarke using `run` is like calling a common method in a class. Using `start` would tell the JVM *hey! there's a new **real** thread that wants to run separate from the main app thread*. The fact that FindBugs mark it as *possible* mistake is that calling `run` on a Thread **should** be a mistake (IMO it is a mistake), it is meant to be called from the JVM, not from the programmer. – Luiggi Mendoza May 17 '13 at 20:00
  • 1
    I guess it depends if you're intending to test the method, or the threading – Gus May 17 '13 at 20:16
  • JUnit5 test runner calls `System.exit` in [`ConsoleLauncher`](https://github.com/junit-team/junit5/blob/e69243ae0f3c68f07d075ac3901c98c375d040f0/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java#L38), Intellij IDEA's runner calls `System.exit` in [`JUnitStarter`](https://github.com/JetBrains/intellij-community/blob/105afaa5ce8bab3b2356f3dadbcfc59632a8caa6/plugins/junit_rt/src/com/intellij/rt/junit/JUnitStarter.java#L55) – Denis Zavedeev Apr 17 '22 at 20:38
6

In the TestThread function, JUnit has no way of telling that you spawned a new thread. It reclaims any objects it created in the function as soon as the function ends (last line is reached) and doesn't know about the thread.

For this reason, you see Output from the Thread until it gets killed. If you want to wait for Thread Completion, you can use Thread.join (right after you call Thread.start()) to make it wait for the result or use wait-notify on some object.

Also, found this question: JUnit terminates child threads

Community
  • 1
  • 1
goblinjuice
  • 3,184
  • 24
  • 26
  • To say that JUnit 'deletes' objects is a little misleading, since in Java, there is no way to explicitly delete an object. – Martin Ellis May 17 '13 at 19:59
  • @Martin right. but JVM can reclaim (GC) when the object is not needed anymore. Once the tests are done, JUnit kills Threads and basically the whole JVM. – goblinjuice May 17 '13 at 20:08
  • @goblinjuice that doesn't mean that JUnit kills an object instance at all. Since the JVM exits, every single memory bit used will be freed. – Luiggi Mendoza May 17 '13 at 20:10
  • It just calls System.exit. There's a link to the source in my answer. – Martin Ellis May 17 '13 at 20:12
0

It's probably that the parent thread running the test is finishing. If you can avoid it then don't create a new thread in you tests. I would create a new class implementing the runnable and then test that class my simply calling run on that object. You shouldn't need to test thread scheduling as part of your tests. Hopefully the java language dev team have that covered :)

RNJ
  • 15,272
  • 18
  • 86
  • 131