任何人都可以为这段代码建议一个 mockito 测试吗?

Can anyone suggest a mockito Test for this code?

此函数尝试使用 GitHub API 检索 GitHub 存储库名称的主题:https://api.github.com/repos/flutter/flutter/topics

public List<String> getTopics(SearchRepository searchRepository){
        GitHubRequest request = new GitHubRequest();
        List<String> topic_list = new ArrayList<>();
        try {

            String url =  searchRepository.getUrl().split("//")[1].split("github.com")[1];

            request.setUri("/repos"+ url + "/topics");
            String result = new BufferedReader(new InputStreamReader(gitHubClient.getStream(request)))
                    .lines().collect(Collectors.joining("\n"));
            JSONObject jsonObject = new JSONObject(result);
            topic_list = Arrays.stream(jsonObject.get("names").toString().replace("[", "").replace("]", "").split(",")).collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return topic_list;
    }

字符串到列表转换前返回的url是:https://github.com/repouser/reponame

SearchRepository 是软件包中 Repository 的 v2 模型 class: SearchRepositroy 根据这个精彩社区答案的早期建议(虽然问题现在已被编辑,但它仍然相关)我创建了模拟测试来测试上述功能,如下所示:

    @Test
    public void getTopicsTest() throws IOException{
        SearchRepository mocksearchRepository = mock(SearchRepository.class);
        GitHubClient mockClient = mock(GitHubClient.class);
        GitHubRequest mockRequest = mock(GitHubRequest.class);
        when(mocksearchRepository.getUrl()).thenReturn("https://github.com/mockuser/mockrepo");
        when(mocksearchRepository.getName()).thenReturn("mockrepo");
        when(mocksearchRepository.getOwner()).thenReturn("mockuser");
        //when(mockRequest.setUri((String) any())).thenReturn(mockRequest.setUri("/repo/mockuser/mockrepo/topics"));
        //The format of the return form is: https://github.com/CyC2018/CS-Notes
        when(mockClient.getStream(any())).thenReturn(topicInputStream());
        //SearchRepository querySearch = new SearchRepository("mockuser","mockrepo");
        List<String> topics = githubServiceMock.getTopics(mocksearchRepository);
        System.out.println(topics);
    }

    private InputStream topicInputStream() {
        String mockTopics = "{" +
                "topic1\": [" +
                "topic2\"," +
                "topic3\"," +
                "skia" +
                "]" +
                "}";
        InputStream stream = new ByteArrayInputStream(mockTopics.getBytes(StandardCharsets.UTF_8));
        System.out.println(stream);
        return stream;
    }

但是,由于函数中的 request.setUri(Uri(String)),我得到 404 内部服务器错误异常。请指出我哪里出错了。

Test services.GithubServiceTest.getTopicsTest started
java.io.ByteArrayInputStream@6ed99482
org.eclipse.egit.github.core.client.RequestException: Not Found (404)
  | => rat org.eclipse.egit.github.core.client.GitHubClient.createException(GitHubClient.java:552)
        at org.eclipse.egit.github.core.client.GitHubClient.getResponseStream(GitHubClient.java:701)
        at org.eclipse.egit.github.core.client.GitHubClient.getStream(GitHubClient.java:667)
        at services.GithubService.getTopics(GithubService.java:231)
        at services.GithubServiceTest.getTopicsTest(GithubServiceTest.java:222)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
        at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
        at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
        at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
        at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
        at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
        at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
        at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
        at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:79)
        at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
        at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
        at org.junit.runners.Suite.runChild(Suite.java:128)
        at org.junit.runners.Suite.runChild(Suite.java:27)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
        at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
        at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
        at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
        at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
        at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
        at com.novocode.junit.JUnitRunner.execute(JUnitRunner.java:132)
        at sbt.ForkMain$Run.lambda$runTest(ForkMain.java:413)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)
[]
[info] Test services.GithubServiceTest.getIssueWordLevelStatisticsTest started
[info] Test services.GithubServiceTest.getRepositoriesByTopicTest started
[info] Test services.GithubServiceTest.getUserDetailsTest started
[info] Test services.GithubServiceTest.getRepositoryDetailsTest started
[info] Test run finished: 0 failed, 0 ignored, 5 total, 1.122s
[error] Failed: Total 10, Failed 1, Errors 0, Passed 9
[error] Failed tests:
[error]         controllers.GithubControllerTest
[error] (Test / test) sbt.TestsFailedException: Tests unsuccessful

您可以模拟 repositoryServicegitHubClient 来测试各种场景。

您可以为这些字段提供包级别 setter。用 Guava VisibleForTesting 注解来注解是个好主意。

然后你需要在对实例进行实际测试之前设置模拟。

模拟伪代码

// First, we mock repository so we can return it from service
Repository repositoryMock = mock(Repository.class);
when(repository.getUrl()).thenReturn("https://...."); // You can return a URL or String, according to the mocked method's return type

// Then, we mock RepositoryService as well
RepositoryService repositoryServiceMock = mock(RepositoryService.class);
when(repositoryServiceMock.getRepository(anyString(), anyString()).thenReturn(repositoryMock);

// We mock gitHubService similarly
...

// After setting mock on the instance, we call the method and see if the results are expected
List<String> topics = getTopics("...", "...");

assertEquals(topics, ...);

// You can also assert interaction related things
verify(repositoryService).getRepository("a", "b"); // This is OK only if the service is called with "a" and "b" arguments

首先,您需要将模拟对象 (repositoryService,gitHubClient) 注入到您的函数中。

如果使用spring/spring引导,可以考虑使用@Mock和@MockBean注解。 模拟必要的 bean。

以下代码手动创建模拟对象。

RepositoryService fakeRs = Mockito.mock(RepositoryService.class);
GitHubClient fakeGHC = Mockito.mock(GitHubClient.class);

您也可以使用setter 方法来设置模拟对象。 setRepositoryService(fakeRs); setGitHubClient(fakeGHC);

另一种方法是使用反射实用程序设置私有对象。

ReflectionTestUtils.setField(yourService, "repositoryService", fakeRs);
ReflectionTestUtils.setField(yourService, "gitHubClient", fakeGHC);

提取完模拟对象后,您可以从模拟对象中编写预期 behavior/data 的测试。

    @Test
    public void testGetTopics(){
       // init mock object code in case setter/getter/reflection utils
       Repository expectedRepository = createExampleRepository();
Mockito.when(repositoryService.getRepository(Mockito.anyString(),Mockito.anyString())
.thenReturn(expectedRepository);

     // continue to fake gitHubClient with your expected data/ exceptions...
    //call your method
    List<?> data = yourService.getTopic("user","data");
    Assertions.assertTrue(data!=null); 
   // you can write few assertion base on your fake datas
    }

PS:不要copy/paste代码,你会得到编译错误。我没有使用编辑器工具。