0

I am working on writing some junit test for my spring application. Below is my junit test that that calls afterPropertiesSet method of my InitializeFramework class that implements InitializingBean interface.

Below is the flow where my junit test calls afterPropertiesSet method and then that method will call initializeModel method within the same class, and then that method has a scheduler which will call getBundlesInfo method every few minutes. But somehow during my junit, getBundlesInfo method is not getting called at all.

@Test
public void testFramework() {


    try {
        InitializeFramework io = new InitializeFramework();
        io.afterPropertiesSet();

    } catch (Exception e) {

    }
}


public class InitializeFramework implements InitializingBean {

private static long checkWithDBInterval = 1L;

private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @Override
    public void afterPropertiesSet() throws Exception {

        try {

        // other code here

        initializeModel();  
        } catch (Exception e) {

        }
    }

    private void initializeModel() {

        final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(
                new Runnable() {
                    public void run() {
                        try {
                            getBundlesInfo();
                        } catch(Exception ex) {
                            // log exception here
                        }
                    }
                }, 0, checkWithDBInterval, TimeUnit.MINUTES);
    }


    // this method is not getting called from my junit test 
    protected static void getBundlesInfo() {

    // some code here
    // have put the breakpoint here..

    }
}

Can anybody help me with this? What wrong I am doing here? but during my application run, this flow works perfectly fine and getBundlesInfo gets called... Only during junit it is not working..

4

2 回答 2

2

发生这种情况是因为您的单元测试在调度程序执行您的 Runnable 之前退出。

您要测试该afterPropertiesSet调用getBundlesInfo还是要测试 的重复调用getBundlesInfo

您的单元测试如何断言 getBundlesInfo 被调用?还是你还没到?

如果您只想看到它getBundlesInfo被调用,您可以直接调用它并将initialDelay调度程序的值增加到checkWithDBInterval,或者getBundlesInfo使用 Mockito 和/或 Powermock 存根,例如使用 CountDownLatch 进行同步。

好吧,或者在调用后等待几秒钟afterPropertiesSet,然后检查是否getBundlesInfo被调用(你也可以使用 Mockito 来做)。

在任何情况下,您都可能希望在测试完成后在执行程序服务上添加调用shutdown 的代码


由于您使用 Spring:
考虑使用提供的任务执行和调度框架来安排重复调用getBundlesInfo并让 afterPropertiesSet 最初直接调用 getBundlesInfo。

无论如何,这是一个使用存根和使用 CountDownLatch 等待部分的示例。我还必须制作getBundlesInfo非静态的,因为它无法快速记住/找到如何存根静态方法。

import static org.mockito.Mockito.*;
import java.util.concurrent.*;

import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class StackOverflowTest
{

    public static class ClassWithScheduler
    {
        private static long checkWithDBInterval = 1L;
        private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1 );

        public void entryPoint()
        {
            scheduler.scheduleAtFixedRate( new Runnable() {
                public void run()
                {
                    try
                    {
                        thisHasToBeCalled();
                    }
                    catch( Exception ex )
                    {
                        // log exception here
                    }
                }
            }, 0, checkWithDBInterval, TimeUnit.MINUTES );

        }

        protected void thisHasToBeCalled()
        {
            System.out.println( "thisHasToBeCalled was called" );
        }
    }

    // since we are waiting on another thread either use a timed-wait (latch.await also
    // has a variant which accepts a timeout) or use the timeout attribute of the
    // @Test annotation
    @Test( timeout = 5000L )
    public void testCall() throws Exception
    {
        // create latch which this thread waits on and the scheduler thread
        // notifies on
        final CountDownLatch latch = new CountDownLatch( 1 );

        // create instance
        ClassWithScheduler instance = spy( new ClassWithScheduler() );

        // stub thisHasToBeCalled to notify on the latch
        doAnswer( new Answer<Void>() {
            @Override
            public Void answer( InvocationOnMock invocation ) throws Throwable
            {
                // call the real method
                invocation.callRealMethod();

                // notify waiting thread
                latch.countDown();

                System.out.println( "stub" );

                return null;
            }
        } ).when( instance ).thisHasToBeCalled();

        // execute
        instance.entryPoint();

        // wait for thread to call the stubbed method
        latch.await();

        // assert that the method was called / 
        verify( instance ).thisHasToBeCalled();
    }
}
于 2013-09-12T20:06:40.533 回答
0

您会注意到,如果您将其更改getBundlesInfo()为类似

    protected static void getBundlesInfo() {
        System.out.println("ay");
    }

并且TimeUnit您正在使用 to TimeUnit.MILLISECONDS,它将尽可能多地打印。例如,我得到了

ay
ay
ay
ay

这是因为 JUnit 在退出之前会清理 JVM 上运行的所有线程。它杀死/停止/打断他们。

于 2013-09-12T19:32:57.657 回答