我在其中有以下类和方法
public class A extends B implements C{
public void validateTicketGrantingTicket(final TicketGrantingTicket ticketGrantingTicket) throws InvalidTicketException {
if (ticketGrantingTicket != null)
{
if (!ticketGrantingTicket.getHostDomain().equalsIgnoreCase(getServerName()))
{
throw new InvalidTicketException();
}
}
}
public String getServerName()
{
String serverName = "";
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null)
{
serverName = request.getServerName().toLowerCase();
}
return serverName;
}
}
现在我正在编写ATest类并模拟A类。
public class ATest {
private A a;
@Before
public void init(){
A = mock(A.class);
when(A.getServerName()).thenReturn("phoenix.edu.abc");
}
@Test
public void validateTicketGrantingTicketTest() throws InvalidTicketException{
a = new A();
ticketGrantingTicket = new
TicketGrantingTicketImpl("test",testUtils.getAuthentication(), new
NeverExpiresExpirationPolicy());
a.validateTicketGrantingTicket(ticketGrantingTicket);
}
Mock对象给了我getServerName()方法的空指针异常,而不是字符串“phoenix.edu.abc”
答案 0 :(得分:7)
通过在测试方法中调用a = new A();
,可以创建A的新实例,并且对在init()
方法中创建的模拟A实例的引用将会丢失。因此,A的getServerName()
方法的“真实”实现将被调用,而不是你所嘲笑的那个。
你遇到的另一个问题是你试图同时模拟和测试同一个类。一方面,您正在测试validateTicketGrantingTicket()
,但同时您正在嘲笑getServerName()
。您可以使用spy()而不是mock()
来解决此问题,但最好将代码重构为两个单独的类。
答案 1 :(得分:0)
我想将方法getServerName移动到另一个类是不是一个好主意,以便可以在其他地方重用它?这样可以轻松测试此方案。
正如马塞夫所说,你也试图嘲笑被测试的课程,这不是一个正确的做法。正如他所指出的那样,使用spy()
可以在特殊情况下使用它。即使mockito在其后期版本中引入了此功能,但强烈建议其用户限制使用spy()
。
如果将方法移动到新类,则可以轻松模拟其他类并返回所需的值。
public class A extends B implements C{
private AUtil util;
public void validateTicketGrantingTicket(final TicketGrantingTicket ticketGrantingTicket) throws InvalidTicketException {
if (ticketGrantingTicket != null)
{
String serverName = util.getServerName()
if (!ticketGrantingTicket.getHostDomain().equalsIgnoreCase(serverName))
{
throw new InvalidTicketException();
}
}
}
}
public class AUtil {
public String getServerName()
{
String serverName = "";
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null)
{
serverName = request.getServerName().toLowerCase();
}
return serverName;
}
}
如果我们将所有实用程序函数移动到公共类(如A的所有类都可以重用它们),那么这可以被认为是一个很好的设计。它现在有一个独立的关注点和更好的可测试性。您可以轻松地模拟此方法而无需间谍。
public class ATest {
//cut provides a uniform naming convention-Class Under Test
private AUtil cut;
//Mock can now be created using annotations in Mockito
@Mock
AUtil mock;
@Test
public void validateTicketGrantingTicketTest() throws InvalidTicketException{
cut = new A();
ticketGrantingTicket = new
TicketGrantingTicketImpl("test",testUtils.getAuthentication(), new
NeverExpiresExpirationPolicy());
when(mock.getServerName()).thenReturn("phoenix.edu.abc");
a.validateTicketGrantingTicket(ticketGrantingTicket);
}
}