Mockito
常用注解
@Mock
@Spy
@InjectMocks
记录一次在使用mockito时候遇到的问题,然后进行仔细研究一番;以下有一段代码,我们的场景是对某个mock对象执行方法内部的方法再次进行mock数据返回时候,碰到的问题,可以一起学习一下。先粘贴一部分代码:
PersonService:
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private BookService bookService;
@Override
public String myBooks() {
String name = "java编程";
System.out.println("我就一本书:java编程");
String book = bookService.findBook(name);
int i = bookNums(name);
return i + "本" +book;
}
public int bookNums(String name){
return 6;
}
}
BookService:
public interface BookService {
String findBook(String name);
}
上述代码我们想对上述方法进行单元测试,当我们需要对一个mock对象中调用的某个方法再次进行插桩时候可以进行实现方式有两种,例如我们想mock方法BookService#findBook返回值以及PersonServiceImpl#bookNums返回值,此时有两种方法: 如果你是新接触的,大概率会写成:
public class PersonTest {
/**
* 使用@InjectMocks可以将@Mock声明的bookService注入到personServiceImpl中进行使用
*/
@InjectMocks
PersonServiceImpl personServiceImpl;
@Mock
BookService bookService;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
@Test
public void findBookTest1(){
when(bookService.findBook(anyString())).thenReturn("good");
when(personServiceImpl.bookNums(anyString())).thenReturn(5);
String s = personServiceImpl.myBooks();
System.out.println(s);
}
}
此时会报错:
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
This message may appear after an NullPointerException if the last matcher is returning an object
like any() but the stubbed method signature expect a primitive argument, in this case,
use primitive alternatives.
when(mock.get(any())); // bad use, will raise NPE
when(mock.get(anyInt())); // correct usage use
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.
原因就是when(personServiceImpl.bookNums(anyString())).thenReturn(5);这行代码中personServiceImpl是真实的对象,而非mock对象。
针对于上述,我们还想对personServiceImpl中的bookNums()进行mock数据时候,有两种方式:
第一种:
使用注解@InjectMocks配合注解@Spy一起使用。使用@Spy代表创建真实对象,使用此注解后我们便可以对真实对象中的某个方法进行插桩,所以再次执行when(personServiceImpl.bookNums(anyString())).thenReturn(5);这行代码便不会报错。
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonTest {
/**
* 使用@InjectMocks可以将@Mock声明的bookService注入到personServiceImpl中进行使用
*/
@InjectMocks
@Spy
PersonServiceImpl personServiceImpl;
@Mock
BookService bookService;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
@Test
public void findBookTest1(){
when(bookService.findBook(anyString())).thenReturn("good");
when(personServiceImpl.bookNums(anyString())).thenReturn(5);
String s = personServiceImpl.myBooks();
System.out.println(s);
}
}
第二种:
两个对象均声明为mock对象,手动将bookService对象注入personServiceImpl对象中,使用Mockito.doCallRealMethod()方法进行执行真实调用某个方法,所以此处的方式改成:
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonTest {
/**
* 此时均声明为mock对象
*/
@Mock
PersonServiceImpl personServiceImpl;
@Mock
BookService bookService;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
//此处手动将bookService对象注入personServiceImpl对象中
ReflectionTestUtils.setField(personServiceImpl,"bookService",bookService);
}
@Test
public void findBookTest1(){
when(bookService.findBook(anyString())).thenReturn("good");
//当mock对象personServiceImpl执行到myBooks()方法时候,则进行真实调用,便可以实现想要的效果
doCallRealMethod().when(personServiceImpl).myBooks();
//对mock对象personServiceImpl.bookNums()进行插桩
when(personServiceImpl.bookNums(anyString())).thenReturn(5);
String s = personServiceImpl.myBooks();
System.out.println(s);
}
}
结论:
被@InjectMocks注解修饰的对象,不是mock对象,是一个真实对象。当通过MockUtil.isMock(object)判断时候返回的是false。
当通过使用Mockito.when(object).thenReturn(null)时候进行插桩时候,要求object必须是mock对象才可以继续执行,否则会报错。