【问题标题】:Non-void method with doNothing() in Mockito?在 Mockito 中使用 doNothing() 的非 void 方法?
【发布时间】:2018-12-07 09:53:22
【问题描述】:

我有一个单元测试来测试将文件上传到 GCP 存储。这是上传文件的代码。

@Override
public boolean upload(StorageConfiguration storageConfiguration, File file) throws MetaFeedException {
    // get google storage connection.
    Optional<Storage> storage = getStorageConnection(storageConfiguration.getJsonCredentialFilePath());

    // if GCP storage is empty, return empty.
    if (!storage.isPresent()) {
        throw new MetaFeedException("Failed to establish connection with GCP storage");
    }

    // upload blob with given file content
    BlobId blobId = BlobId.of(storageConfiguration.getBucketName(),
            storageConfiguration.getBucketPath().concat(file.getName()));
    BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(storageConfiguration.getContentType()).build();
    try {
        return storage.get().create(blobInfo, Files.readAllBytes(Paths.get(file.getAbsolutePath()))).exists();
    } catch (Exception e) {
        throw new MetaFeedException("Error occurred while uploading file.", e);
    }
}

在我的单元测试中,我做过这样的事情,

@Test
public void should_upload_file_to_gcp_with_given_data() throws Exception {
    File tempFile = File.createTempFile("file-name", "1");

    StorageConfiguration storageConfiguration = new StorageConfiguration();
    storageConfiguration.setBucketName("sample-bucket");
    storageConfiguration.setBucketPath("ff/");
    storageConfiguration.setJsonCredentialFilePath("json-credentials");
    storageConfiguration.setContentType("text/plain");

    StorageOptions defaultInstance = mock(StorageOptions.class);
    Storage mockStorage = spy(Storage.class);

    when(defaultInstance.getService()).thenReturn(mockStorage);

    BlobId blobId = BlobId.of(storageConfiguration.getBucketName(), storageConfiguration.getBucketPath().concat(tempFile.getName()));
    BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(storageConfiguration.getContentType()).build();

    doNothing().when(mockStorage).create(blobInfo, Files.readAllBytes(Paths.get(tempFile.getAbsolutePath())));

    boolean upload = gcpStorageManager.upload(storageConfiguration, tempFile);
    Assert.assertTrue(upload);
}

我想要做的是阻止调用create() 方法。我的意思是我不想真正上传到 GCP,因为它是一个测试。所以我按照上面的方法尝试了。但是我遇到了一个错误,

org.mockito.exceptions.base.MockitoException: 
Only void methods can doNothing()!
Example of correct use of doNothing():
    doNothing().
    doThrow(new RuntimeException())
    .when(mock).someVoidMethod();
 Above means:
someVoidMethod() does nothing the 1st time but throws an exception 
the 2nd time is called

更新

Optional<Storage> storage;
    try {
        //connect with the json key file if the key path is not empty
        if (StringUtils.isNotEmpty(jsonCredentialFilePath) && Files.exists(Paths.get(jsonCredentialFilePath))) {
            storage = Optional.ofNullable(StorageOptions.newBuilder()
                    .setCredentials(ServiceAccountCredentials.fromStream(new FileInputStream(jsonCredentialFilePath)))
                    .build().getService());
        } else {
            // if no json key file provided connect to storage without key file.
            storage = Optional.ofNullable(StorageOptions.getDefaultInstance().getService());
        }
    } catch (Exception e) {
        throw new MetaFeedException("Error occurred while connecting to GCP storage", e);
    }

    return storage;

有没有办法修复这个上传文件到 GCP 的测试?

【问题讨论】:

    标签: java junit google-cloud-platform mockito google-cloud-storage


    【解决方案1】:

    我遇到了以下问题: 我有一个返回 Page<...> 的方法,但我只能在 void 方法上使用 doNothing() ,所以修复很容易:

        @Override
        public Page<ThreadServiceModel> findAll(Pageable pageable) {
            return this.findAll(ThreadServiceModel.class, pageable);
        }
    

    我们开始吧:

            //Will fail and the actualy findAll will be invoked:
            when(this.threadServices.findAll(ThreadServiceModel.class,pageable)).thenReturn(null);
    
            //Will fail ,cuz you cannot call doNothing() on something that returns values
    
            doNothing().when(this.threadServices).findAll(ThreadServiceModel.class,pageable);
    
            //This is the solution ,and when you use doReturn() the actual method will be NOT invoked,
            //+In my case i dont need value , but if i want I cant use...
            doReturn(null).when(this.threadServices).findAll(ThreadServiceModel.class,pageable);
    

    所以解决方案很简单,在我展示给你的代码上使用最后一种方法:)

    【讨论】:

    • 如果我不想返回 null 因为它会抛出空指针
    【解决方案2】:

    错误信息中描述了实际问题:Only void methods can doNothing()

    create() 方法不是 void,所以你不能用 doNoting() 模拟它,所以你必须改用 doReturn。在您的情况下,您可以返回 Blob 的模拟:

    Blob mockBlob = mock(Blob.class);
    when(mockBlob.exists()).thenReturn(true);
    doReturn(mockBlob).when(mockStorage).create(blobInfo, Files.readAllBytes(Paths.get(tempFile.getAbsolutePath())));
    

    这是第一个错误。第二,看起来你没有将间谍对象注入你的被测对象。 您的方法getStorageConnection() 将在您调用它时创建Storage 的一个新实例,并且不考虑间谍对象。 所以你在模拟 StorageOptions.classStorageOptions.getDefaultInstance() 方法对这个模拟对象一无所知。

    所以在这里我可以看到解决问题的两条途径。第一个,对我来说更可取的是向方法或类注入依赖项。 方法的依赖注入:

    @Override
    public boolean upload(Storage storage, File file) throws MetaFeedException {
    

    但情况可能并非如此,只要您的方法覆盖某些接口,正如我假设的那样。因此,您可以将您的方法getStorageConnection 移动到一个单独的类StorageProvider 并将其实例注入您的类,然后在测试中您可以模拟StorageProvider 以返回spyStorage。在上传方法中你会调用:

    Optional<Storage> storage = storageProvider.getStorageConnection(storageConfiguration.getJsonCredentialFilePath());
    

    第二条路径,相当解决方法是使方法getStorageConnection 受保护,并在您的测试中覆盖它以返回一个间谍对象。在你的测试中:

    gcpStorageManager = new GcpStorageManager() {
      @Override
      protected Storage getStorageConnection(...) {
        return mockedStorage;
      }
    }
    

    【讨论】:

    • Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden 我收到这个是因为它仍在尝试上传文件。
    • 方法create()是否返回BlobBucket。你必须确保你存根需要的方法而不是重载的方法。
    • 它正在返回一个 blob。
    • 你确定窥探到的storage与方法中使用的相同吗?
    • 另外,你真的需要spy吗?为什么不使用mock
    猜你喜欢
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多