如何通过在 Spring 中调用外部 API 来测试服务

How do I test a Service with calls to an external API in Spring

好的,所以我对测试和 Spring 一般的引导还很陌生,所以如果我一开始在这里做错了什么,请纠正我。

作为一个项目,我和我的团队正在使用 Spring Boot 创建一个 Web 应用程序,我们在某些服务中调用 Microsoft Graph API。查看此服务以取消用户日历中的事件:

import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.models.extensions.IGraphServiceClient;
import com.microsoft.graph.requests.extensions.GraphServiceClient;
import org.springframework.stereotype.Service;


@Service
public class CancelEventService {

    public void cancelEvent(final String token, final String id) {

        IAuthenticationProvider authenticationProvider = iHttpRequest -> iHttpRequest.addHeader("Authorization", "Bearer " + token);
        IGraphServiceClient graphServiceClient = GraphServiceClient.builder().authenticationProvider(authenticationProvider).buildClient();

        graphServiceClient.me().events(id)
                .buildRequest()
                .delete();
    }
}

这很好用,但我已经为如何为此编写单元测试苦苦挣扎了几天。在我看来,我想模拟 GraphServiceClient,或者使用像 WireMock 这样的工具将请求发送到模拟服务器和 return 我配置的一些值。

我都试过了,但我无法模拟 GraphServiceClient,因为它不是我项目中的 Bean,所以我无法弄清楚我应该如何继续进行我可以自动装配的实现到我的服务。

当谈到 WireMock 时,我什至不确定我是否理解它是否能够做我想做的事情,如果是,我肯定无法正确配置它(注意我们正在使用本项目的 JUnit 5)。一个简单的示例,您通过 WireMock 向 Google.com 和 return "Hello" 发出 GET 请求就足以让我转到这里。

任何关于我应该做什么的具体例子,或者只是在正确的方向上点头,我将不胜感激。

好吧,我不能向你保证它会起作用,但它会让你更好地了解情况:

1) 首先,我们需要对您的服务稍作改动。 需要从方法中提取 IGraphServiceClient 以便我们稍后模拟它,参见:

@Service
public class CancelEventService {

   private IGraphServiceClient graphServiceClient;

   @Autowired
   public CancelEventService(IGraphServiceClient graphServiceClient){
       this.graphServiceClient = graphServiceClient;
   }

    public void cancelEvent(final String token, final String id) {

        IAuthenticationProvider authenticationProvider = iHttpRequest -> iHttpRequest.addHeader("Authorization", "Bearer " + token);
        graphServiceClient = GraphServiceClient.builder().authenticationProvider(authenticationProvider).buildClient();

        graphServiceClient.me().events(id)
                .buildRequest()
                .delete();
    }
}

2) 测试将如下所示: (请注意,我们在这里使用的所有内容都包含在 spring 启动测试模块中,因此您不需要向项目依赖项添加任何内容)

@RunWith(SpringRunner.class)
public class CancelEventServiceTest {

  private IGraphServiceClient graphServiceClientMock;

  private CancelEventService serviceToBeTested;

  @Before
  public void setUp(){

    graphServiceClientMock = Mockito.mock(IGraphServiceClient.class, RETURNS_DEEP_STUBS);
    serviceToBeTested = new CancelEventService(graphServiceClientMock);
  }

  @Test
  public void test_1() {


    serviceToBeTested.cancelEvent("token", "id");

    verify(graphServiceClientMock, times(1)).me().events("id").buildRequest()
        .delete();
  }
}

希望对您有所帮助!

作为对此的跟进,我们通过创建 class 找到了解决问题的方法 GraphServiceClientImpl 具有 returns 实例化 GraphServiceClient 的方法。

@Component
public class GraphServiceClientImpl {

    public IGraphServiceClient instantiateGraphServiceClient(final String token) {
        IAuthenticationProvider authenticationProvider = iHttpRequest -> iHttpRequest.addHeader("Authorization", "Bearer " + token);
        return GraphServiceClient.builder().authenticationProvider(authenticationProvider).buildClient();
    }
}
@Service
public class CancelEventService{

    private GraphServiceClientImpl graphServiceClientImpl;

    public CancelEventService(final GraphServiceClientImpl graphServiceClientImpl) {
        this.graphServiceClientImpl = graphServiceClientImpl;
    }

    public void cancelEvent(final String token, final String id) {

        IGraphServiceClient graphServiceClient = graphServiceClientImpl.instantiateGraphServiceClient(token);

        graphServiceClient
                .me()
                .events(id)
                .buildRequest()
                .delete();
    }
}

然后,我们的测试:

@ExtendWith(MockitoExtension.class)
class CancelEventServiceTest {

    @Mock
    private GraphServiceClientImpl graphServiceClientImpl;

    @InjectMocks
    private CancelEventService cancelEventService;

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

    @Test
    void cancelEvent_Successful() {

        //given
        IGraphServiceClient serviceClientMock = mock(IGraphServiceClient.class, RETURNS_DEEP_STUBS);
        given(graphServiceClientImpl.instantiateGraphServiceClient(anyString())).willReturn(serviceClientMock);

        //when
        cancelEventService.cancelBooking("Token", "1");

        //then
        verify(serviceClientMock, times(1)).me();
    }
}

可能不是最佳解决方案,但它有效。欢迎任何其他人对此采取行动!