I have a unit test to test uploading a file to GCP storage. Here is the code for upload the file.
@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);
}
}
In my unit tests, I've done something like this,
@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);
}
What I'm trying to do is prevent calling the create()
method. My point is I don't want to perform a real upload to GCP since it's a test. So I tried as above. But I got an error,
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
UPDATE
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;
Is there a way to fix this test for uploading file to GCP?
I had the following problem: I had a method that was returning a Page<...> ,but I can use doNothing() only on void method ,so the fix is easy:
@Override
public Page<ThreadServiceModel> findAll(Pageable pageable) {
return this.findAll(ThreadServiceModel.class, pageable);
}
And here we go:
//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);
So the solution is simple ,use the last method on the code that I have showed you :)
The actual problem is described in the error message: Only void methods can doNothing()
The create()
method is not void, so you cannot mock it with doNoting()
so you have to use doReturn
instead. In you case you can return a mock of Blob
:
Blob mockBlob = mock(Blob.class);
when(mockBlob.exists()).thenReturn(true);
doReturn(mockBlob).when(mockStorage).create(blobInfo, Files.readAllBytes(Paths.get(tempFile.getAbsolutePath())));
This is the first mistake. And the second, it looks like you are not injecting the spied object into you object-under-test.
Your method getStorageConnection()
will create a new instance of Storage
when you call it and does not take into account the spied object.
So you are mocking the StorageOptions.class
but the StorageOptions.getDefaultInstance()
method don't know anything about this mocked object.
So here I can see two paths to fixing your problem. The first one, and it's a more preferable one as for me is to inject dependency either to a method or to your class. Dependency injection to method:
@Override
public boolean upload(Storage storage, File file) throws MetaFeedException {
But this may not be the case, as long as your method overrides some interface, as I assume. So you can move your method getStorageConnection
to a separate class StorageProvider
and inject the instance of it to you class, and then in the test you can mock a StorageProvider
to return a spy
of a Storage
. In the upload method you will call:
Optional<Storage> storage = storageProvider.getStorageConnection(storageConfiguration.getJsonCredentialFilePath());
The second path, which is rather workaround is to make the method getStorageConnection
protected, and the override it in your test to return a spied object. In your test:
gcpStorageManager = new GcpStorageManager() {
@Override
protected Storage getStorageConnection(...) {
return mockedStorage;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With