mockito 测试调用抽象执行器

mockito test invocation into abstract executor

我正在对遗留代码编写测试,在重构它之前,这意味着 我不应该更改该代码... 我正在使用 Spring 和 Hazelcast,而我的 class 是一个 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也会被调用。 我正在尝试不同的方法,但所有方法都遇到了一些 mockito 错误。这是最后一个:

@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 是一个抽象的 class。我不能监视它,我不能调用真正的方法。此外,@Qualifier 指的是库中的实现,或多或少是:

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

有什么想法吗?

您正在尝试使用过多的模拟。您在嘲笑 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);
}