1

I am trying to write test case for Threads, which is written by someone else.

I don't have liberty of changing the existing code.I have gone through various threads in this forum but nothing seems to be working for this scenario. Please suggest.

My code is like below:

Class X implements Runnable{
   Y y;
   public X(){}

    public X(Y y){
      this.y = y;
    } 

    public void run(){
    Y.staticVoidMethod(A a, B b, boolean c);
    }
}

Then in another class class Main below line is written:

class Main{
    public static void main1(){     
    Y y=new Y();
    new Thread(new X(y)).start();
    }
}

My Test class is like below:

class Test {    
  Thread thread;
  X x;
  Y y = spy(new Y());

  @BeforeMethod(alwaysRun = true)
  public void setup() throws Exception {
                    MockitoAnnotations.initMocks(this);
                    PowerMockito.mockStatic(Y.class);

        x= spy(new X(y));
        thread = spy(new Thread(x));

whenNew(X.class).withAnyArguments().thenReturn(x);
                    whenNew(Thread.class).withArguments(x).thenReturn(thread);      // I have also tried ......withArguments(X.class).thenReturn(thread)

        }

        @Test
        public void test1(){
                Main.main1();
           //none of the below is working
           verify(thread).start();
           verify(x).run();
           assertTrue(thread.isAlive());
      } 
}
GhostCat
  • 137,827
  • 25
  • 176
  • 248
RAHUL ROY
  • 126
  • 2
  • 13
  • 7
    Don't unit test `Thread.start`: you can rest assured that works. Unit test `X.run()`. – Andy Turner Jun 19 '17 at 10:20
  • 1
    WHY do you need to unit test `Thread.start`? There is already a huge test suite in JDK itself, you can trust that suite. – M. Prokhorov Jun 19 '17 at 10:21
  • verify(x).run(); was not working also assertTrue*thread.isAlive()) was not working so I went for verify(thread).start(); But none of these are working. – RAHUL ROY Jun 19 '17 at 10:25
  • 1
    `new X(y))` will not even compile given the code you have posted. Unclear what you're asking. – user207421 Jun 19 '17 at 10:26
  • @EJP I have edited the code. My basic requirement is how to write unit test case for this scenario. Like When i call Main.main1() from test case method. I want to put some assertion or verification so that I can know that thread creation and execution happened successfully. – RAHUL ROY Jun 19 '17 at 10:36
  • 1
    Don't test the platform. Test your application. `Thread.start()` isn't going to fail on you. If it did, nothing would work, probably starting with the compiler. The only way `X.run()` can fail is by throwing a `RuntimeException`. Test that. – user207421 Jun 19 '17 at 10:38
  • @EJP,@M. Prokhorov, I will check whether any exception is thrown or not. – RAHUL ROY Jun 19 '17 at 10:39

2 Answers2

2

As a commenter said, don't test Thread.start() - it works.

You can test your class with X.run():

In principle:

@Test
public void runs() {
    Y y = ...; // could be a mock
    X x = new X(y);

    x.run();

    assertThat( ... probably something about y ...);
}

Your real problem is that you're calling a static method. Static methods tend to be difficult to test, which is just one reason they're often a bad design choice.

Note that in the code you've supplied, you pass in an instance of Y but then you don't use it. The static call Y.staticVoidMethod() has nothing to do with the instance of Y, y, that you pass in.

Mocking static methods with Mockito may help.

slim
  • 40,215
  • 13
  • 94
  • 127
  • Thanks for suggestion. Here I have given a snippet of the class X. Its a huge class in other methods, code is using Y, but it is not relevant to this, Since it was a constructor. So I had to put it here otherwise (as pointed by EJP) It was a compilation error. – RAHUL ROY Jun 19 '17 at 10:52
  • 1
    This is why it's better to create mcve rather than include all the irrelevant bulk of your real-world code. – slim Jun 19 '17 at 11:01
  • Good advice.I will keep it in mind from next time. – RAHUL ROY Jun 19 '17 at 11:21
  • Nice answer ... also nice that you left out a few things that allowed me to write up another answer ;-) – GhostCat Jun 19 '17 at 11:37
1

The other answer/comments are correct. But there is one important aspect here: in order to test the given code, you would need to make such kind of testing.

In other words: there are two things to test. You first create a class XTest that with all the meaningful tests for X inside. (which might require you to use PowerMock(ito) to test that static method call).

Then, you could write a MainTest class that ensures that Main.main() does what it is expected to do. And that would be: creating an instance of X, and passing that to a new thread. And yes, testing that would implicitly test Thread.start() along the way. And you also need PowerMock(ito) for that new X() call in there.

The important thing to understand: you don't need to verify that Thread.start() works; but your testcase might have to know about Thread.start() is called; to ensure that it can do its job.

But: most likely, you are wasting your time. That incoming code you are supposed to test seems to be of bad quality. From there: what is the point of testing code ... when you are not allowed to change the production code? What would happen if your test finds a bug? Leave that bug in the code?!

So the real answer reads more like: stand up to the people that gave you this task and clarify requirements. Because the current requirements do not make sense. And maybe ask them to watch this - so they learn how to write testable code in the future. You are about do fight symptoms of a bad design. Whereas the better answer is most often, to fix the bad design; instead of working around the problems caused by that design!

Alone the idea that X has a Y member field y ... to then make a static call on Y is really strange ...

GhostCat
  • 137,827
  • 25
  • 176
  • 248