Java 尝试使用ApplicationContext时的PowerMockito空指针

Java 尝试使用ApplicationContext时的PowerMockito空指针,java,junit4,powermock,Java,Junit4,Powermock,我有一个类名ServiceLocator public class ServiceLocator implements ApplicationContextAware { private transient ApplicationContext _applicationContext; private static ServiceLocator _instance = new ServiceLocator(); public void setApplicationCon

我有一个类名ServiceLocator

public class ServiceLocator implements ApplicationContextAware {
    private transient ApplicationContext _applicationContext;
    private static ServiceLocator _instance = new ServiceLocator();

    public void setApplicationContext(ApplicationContext applicationContext) 
                            throws BeansException {
        _instance._applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return _instance._applicationContext;
    }

    public static Object findService(String serviceName) {
        return _instance._applicationContext.getBean(serviceName);
    }
}
我试图使用该类在Approver类方法中查找服务

public class ApproverService extends AbstractDataService implements  IApproverService {
     public void updateCompletedInboxStatus(String status) {
        IInboxService inboxService = (IInboxService)ServiceLocator.findService("inboxService");
        InboxItem inboxItem = inboxService.getInboxItem("test");
        inboxItem.setWorkItemStatus(status);
        inboxService.saveInboxItem(inboxItem);
    }
}
有了这些代码,我正试图用PowerMockRunner编写Junit

@RunWith(PowerMockRunner.class)
@PrepareForTest({ApproverService.class})
public class ApproverServiceTest  {
    @InjectMocks
    ApproverService approverService;

    @Mock
    IInboxService inboxService;

    @Mock
    ServiceLocator serviceLocator;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void updateCompletedInboxStatus() {
        RequestAccessHeader reqHdr = new RequestAccessHeader();
        reqHdr.setRequestStatus(AccessConstants.REQ_STATUS_HOLD_INT);
        String status = "test";

        PowerMockito.mockStatic(ServiceLocator.class);
        when(serviceLocator.findService("inboxService")).thenReturn(inboxService);

        approverService.updateCompletedInboxStatus(status);
    }
}
但我得到了空指针

java.lang.NullPointerException 位于com.alnt.fabric.common.ServiceLocator.findService(ServiceLocator.java:25) 位于com.alnt.access.approver.service.ApproverServiceTest.updateCompletedInboxStatus(ApproverServiceTest.java:80)


请帮我找到解决这个问题的办法

静态方法显然没有被模仿

问题很可能是因为您没有在
@PrepareForTest

将其更改为
@PrepareForTest({ApproverService.class,ServiceLocator.class})


非主题:

虽然可以编译,但按实例引用调用静态方法并不是一个好的实践。因此,行应该是
when(ServiceLocator.findService(…)。然后返回(inboxService)


另一个问题是,您试图使用单例模式,但方式错误。假设一个单例返回一个实例,以便调用方可以调用其实例方法。您的
findService
最好是一个实例方法,称为
ServiceLocator.getInstance().findService(…)
。为了进一步改进,除非您真的需要它成为一个单例,否则您应该将它设置为一个普通的对象实例,并将其注入到需要它的对象中(假设您已经在使用Spring,我看没有理由创建单例)

静态方法的设置没有正确模拟

@RunWith(PowerMockRunner.class)
@PrepareForTest({ServiceLocator.class}) //Prepare static class for mock
public class ApproverServiceTest  {    
    @Mock
    IInboxService inboxService;

    @Mock
    InboxItem item;

    @InjectMocks
    ApproverService approverService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void updateCompletedInboxStatus() {
        //Arrange
        String status = "test";

        PowerMockito.mockStatic(ServiceLocator.class);
        when(ServiceLocator.findService("inboxService")) //<-- NOTE static call
            .thenReturn(inboxService);

        when(inboxService.getInboxItem("test")).thenReturn(item);

        //Act
        approverService.updateCompletedInboxStatus(status);

        //...
    }
}
这样一来,主题类就可以真正了解正确执行其功能所需的内容

然后可以相应地重构测试

@RunWith(PowerMockRunner.class)    
public class ApproverServiceTest  {    
    @Mock
    IInboxService inboxService;

    @Mock
    InboxItem item;

    @InjectMocks
    ApproverService approverService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void updateCompletedInboxStatus() {
        //Arrange
        String status = "test";    
        when(inboxService.getInboxItem("test")).thenReturn(item);

        //Act
        approverService.updateCompletedInboxStatus(status);

        //...
    }
}

它看起来不正确:异常来自ServiceLocator.findService的原始代码,这意味着方法调用没有被模拟。你有没有试过
when(ServiceLocator.findService(…)。然后返回(inboxService)
?是的,两种方法都试过了,但是没有运气你真的不应该把
ServiceLocator
和Spring混在一起。我已经发布(并删除)了类似的答案,因为我确实记得编译器将把
instance.staticMethod
编译成
ClassName.staticMethod
,这应该会给出相同的行为。然而,我确实认为静态方法没有被模仿,因为从堆栈跟踪来看,原始代码仍然在调用。我不认为我们可以假设
ApproverService
准备没有在其他测试中使用(尽管可以合理地假设),这就是为什么我故意将其保留在那里。
@RunWith(PowerMockRunner.class)    
public class ApproverServiceTest  {    
    @Mock
    IInboxService inboxService;

    @Mock
    InboxItem item;

    @InjectMocks
    ApproverService approverService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void updateCompletedInboxStatus() {
        //Arrange
        String status = "test";    
        when(inboxService.getInboxItem("test")).thenReturn(item);

        //Act
        approverService.updateCompletedInboxStatus(status);

        //...
    }
}