【问题标题】:How do I test a Service with calls to an external API in Spring如何在 Spring 中通过调用外部 API 来测试服务
【发布时间】:2023-03-19 21:30:01
【问题描述】:

好的,总的来说,我对测试和 Spring Boot 还很陌生,所以如果我首先在这里做错了什么,请纠正我。

作为一个项目,我和我的团队正在使用 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 之类的工具将请求发送到模拟服务器并返回我配置的一些值。

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

当谈到 WireMock 时,我什至不确定我是否了解它是否能够做我想做的事情,如果是的话,我肯定无法正确配置它(请注意,我们使用的是 JUnit 5对于这个项目)。一个简单的示例,您向 Google.com 发出 GET 请求并通过 WireMock 返回“Hello”就足够了。

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

【问题讨论】:

    标签: java spring-boot microsoft-graph-api junit5 wiremock


    【解决方案1】:

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

    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 boot 测试模块中,因此您不需要向项目依赖项中添加任何内容)

    @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();
      }
    }
    

    希望对你有帮助!

    【讨论】:

    • 可能你应该对 IAuthenticatorProvider 做同样的事情,告诉我,我会更正帖子
    • 您好,感谢您的回答!您的解决方案的问题,就像我在说“我无法模拟 GraphServiceClient 因为它不是我的项目中的 Bean”时可能解释得不好,是我没有 IGraphServiceClient 的实现,这是一个由提供的接口我们用于与 Graph API 通信的 Microsoft 依赖项。尝试按照您的建议设置自动装配的构造函数时,我收到错误“无法自动装配。找不到 IGraphServiceClient 类型的 bean”,因为在我的项目中没有具体的实现。有什么建议吗?
    【解决方案2】:

    作为对此的跟进,我们通过创建一个类找到了问题的解决方案 GraphServiceClientImpl 带有一个返回实例化 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();
        }
    }
    

    可能不是最佳解决方案,但它确实有效。任何其他对此的看法都将受到欢迎!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-06
      • 2016-07-19
      • 2022-12-05
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      相关资源
      最近更新 更多