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);
}
我正在对遗留代码编写测试,在重构它之前,这意味着 我不应该更改该代码... 我正在使用 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);
}