mockito测试调用到抽象执行器

时间:2018-04-20 09:54:18

标签: java spring mockito hazelcast executor

我正在编写遗留代码测试,在重构之前,这意味着我不应该更改代码 ... 我正在使用Spring和Hazelcast,我的班级是一个Hazelcast Listener(如果你不知道Hazelcast它没关系,只要知道我不能改变方法的公司)。

@Component
public class MyClass implements EntryAddedListener<String, MyEntry> {

    @Autowired
    @Qualifier( "asyncExecutor" )
    private Executor executor;

    @Autowired
    private ClassToBeCalled classToBeCalled;

    @Override
    public void entryAdded( final EntryEvent<String, MyEntry> event ) {
        executor.execute( () -> {
                ...
                ...
                classToBeCalled.methodToBeCalled( event.getValue() );
            }
        } );
    }
}

我想测试一下,调用entryAdded时,会调用execute,特别是调用methodToBeCalled。 我正在尝试不同的方法,但所有这些方法都会遇到一些模拟错误。这是最后一个:

@RunWith(MockitoJUnitRunner.class)
public class MyClass {

    @Mock
    private Executor asyncExecutor;

    @Mock
    private ClassToBeCalled classToBeCalled;

    @InjectMocks
    private MyClass myClass;

    @Test
    public void entryListenerShouldInvokeTheClassToBeCalled(){
        // given
        EntryEvent entryEvent = mock(EntryEvent.class);
        MyEntry value = mock(MyEntry.class);
        when(entryEvent.getValue()).thenReturn(value);

        // here some of my tries, all commented because they don't work
        // doCallRealMethod().when(asyncExecutor).execute(any(Runnable.class));
        // when(executor.execute(any(Runnable.class))).thenCallRealMethod();

        // when
        myClass.entryAdded(entryEvent);

        // then
        verify(asyncExecutor, times(1)).execute(any(Runnable.class));
        verify(classToBeCalled, times(1)).methodToBeCalled(value);
    }
}

基本上我无法验证是否调用了methodToBeCalled,因为Executor是一个抽象类。我不能间谍,我不能称之为真正的方法。此外,@ Qualifier指的是库中的实现,或多或少是:

    @Bean(name = {"AsyncExecutor"})
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        ...
        ...
        executor.initialize();
        return executor;
    }

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

你正试图使用​​太多的嘲笑。你正在嘲笑Executor,但仍然期望它表现为一个明显不起作用的常规Executor

而是使用SyncTaskExecutor基本上调用executor.execute进行测试的同步调用,并仅模拟ClassToBeCalled

这样的事情,使用ReflectionTestUtils应该可以解决问题。

public class MyClass {

    private Executor executor = new SyncTaskExector();
    private ClassToBeCalled classToBeCalled;

    private MyClass myClass;

    @Before
    public setup() {
       myClass = new MyClass();
       classToBeCalled = mock(ClassToBeCalled.class);
       RelfectionTestUtils.setField(myClass, "executor", executor);
       RelfectionTestUtils.setField(myClass, "classToBeCalled", classToBeCalled);
    }

    @Test
    public void entryListenerShouldInvokeTheClassToBeCalled(){
        // given
        EntryEvent entryEvent = mock(EntryEvent.class);
        MyEntry value = mock(MyEntry.class);
        when(entryEvent.getValue()).thenReturn(value);

        // when
        myClass.entryAdded(entryEvent);

        // then
        verify(classToBeCalled, times(1)).methodToBeCalled(value);
    }
}

调用methodToBeCalled的事实也证明execute方法已被执行。

提示:我建议将其更改为使用基于构造函数的注入而不是字段注入,因为这样可以更轻松地进行测试。

@Component
public class MyClass implements EntryAddedListener<String, MyEntry> {

    private final Executor executor;
    private final ClassToBeCalled classToBeCalled;

    @Autowired
    public MyClass(@Qualifier("asyncExecutor") Executor executor, ClassToBeCalled classToBeCalled) {
        this.executor=executor;
        this.classToBeCalled=classToBeCalled;
    }
}

现在您可以移除ReflectionTestUtils并简单地构建您的对象。

@Before
public void setup() {
   classToBeCalled = mock(ClassToBeCalled.class);
   myClass = new MyClass(executor, classToBeCalled);
}