Mockito:调用方法里面的方法

时间:2011-12-25 10:13:24

标签: mockito

我在其中有以下类和方法

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”

2 个答案:

答案 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);
}
}