How to mock Mono.from(commandResult).block(); - java

I have a class that invokes mongodb using reactive library and am writing a unit test that includes this private method. How to mock Mono.from(commandResult).block() using mockito?
private Document runCommand(final String command) {
final MongoDatabase db = mongoClient.getDatabase("admin");
final Bson bsonDocument = Document.parse(command).toBsonDocument();
final Publisher<Document> commandResult = db.runCommand(bsonDocument);
return Mono.from(commandResult).block();
}

Retuning Mono.just() worked
#Test
void initializeDBTest() {
final Document bsonDoc = Document.parse(isCSResponse);
Mockito.when(mongoClient.getDatabase("admin")).thenReturn(db);
Mockito.when(db.runCommand(Mockito.any())).thenReturn(Mono.just(bsonDoc));
manageDB.initializeDB();
}

Related

#Qualifier not working in unit test, while injecting in constructor

I have a application whose unit tests work correctly. I am trying to move the application to use dependency injection instead of using #AutoWired annotation.
After I make the change, some unit tests fail randomly(No changes have been made to the tests, just the dependency injection part of the application has been changed). I am assuming that the system is unable to create the AmazonS3 client correctly when the dependencies are injected.
See screenshot below, I am trying to create a s3ClientAlphaBeta instance but the application is creating a s3ClientGammaProd instance.
What could I be missing?
Abreviated code:
S3Accessor.java:
#Component
public class S3Accessor {
private AmazonS3 s3ClientAlphaBeta;
private AmazonS3 s3ClientGammaProd;
#Inject
public S3Accessor(#Qualifier("s3ClientAlphaBeta") AmazonS3 s3ClientAlphaBeta,
#Qualifier("s3ClientGammaProd") AmazonS3 s3ClientGammaProd) {
this.s3ClientAlphaBeta = s3ClientAlphaBeta;
this.s3ClientGammaProd = s3ClientGammaProd;
}
public String getHtmlContent(final String deviceType, final String s3Key, final String region) {
final String filePath = generateFilePath(deviceType.toLowerCase(Locale.ROOT), s3Key);
AmazonS3 amazonS3 = s3ClientAlphaBeta;
final String regionSpecificLegoBucket = S3BUCKET_NAME_BETA;
final AmazonS3 regionSpecificAmazonS3 = amazonS3;
return this.getHtmlContentFromS3(regionSpecificLegoBucket, filePath, regionSpecificAmazonS3);
}
private String getHtmlContentFromS3(final String bucketName, final String filePath, final AmazonS3 amazonS3) {
String s3HtmlContent = amazonS3.getObjectAsString(bucketName, filePath);
return s3HtmlContent;
}
S3AccessorTest.java:
public class S3AccessorTest {
#InjectMocks
private S3Accessor s3Accessor;
#Spy
#Qualifier("s3ClientAlphaBeta")
private AmazonS3 s3ClientAlphaBeta;
#Spy
#Qualifier("s3ClientGammaProd")
private AmazonS3 s3ClientGammaProd;
private static final String TEST_VALID_STRING_DESKTOP = "<html><body>Some Content For DESKTOP</body></html>";
private static final String TEST_VALID_WRAPPED_STRING_DESKTOP =
PAINTERS_LEGO_OPENING_DIV + TEST_VALID_STRING_DESKTOP + PAINTERS_LEGO_CLOSING_DIV;
private static final String TEST_VALID_S3_KEY = "/test/.1/main";
private static final String SLASH = "/";
static {
AppConfig.destroy();
AppConfig.initialize("TestApp",
"TestGroup",
new String[] { "--root=configuration/", "--domain=test", "--realm=Ramu" });
}
#BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
s3Accessor.setup();
}
#Test
public void getHtmlContentForDesktop() {
// Arrange
when(s3ClientAlphaBeta.getObjectAsString(anyString(), eq(TEST_VALID_S3_KEY + SLASH + DESKTOP_VIEW)))
.thenReturn(TEST_VALID_STRING_DESKTOP);
// Act
final String outputString = s3Accessor.getHtmlContent(DESKTOP, TEST_VALID_S3_KEY, US_ALPHA_BETA_REGION);
// Assert
assertEquals(TEST_VALID_WRAPPED_STRING_DESKTOP, outputString);
}
CustomRootConfig.java:
#Configuration
#ComponentScan("com.service.controller")
#Import({SmokeTestBeanConfiguration.class })
public class CustomRootConfig {
private static final String NA_S3_SESSION_NAME = "S3Content";
private static final int S3_SESSION_REFRESH_TIME = 3600;
private static final String S3BUCKET_ARN_BETA = "arn:aws:iam::471963992020:role/ProdAccessForAccount";
private static final String S3BUCKET_ARN_PROD = "arn:aws:iam::568948772290:role/ProdAccessForAccount";
#Bean(name = "s3ClientAlphaBeta")
#Singleton
public AmazonS3 s3ClientAlphaBeta() {
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.valueOf(US_ALPHA_BETA_REGION))
.withCredentials(new STSAssumeRoleSessionCredentialsProvider
.Builder(S3BUCKET_ARN_BETA, NA_S3_SESSION_NAME)
.withRoleSessionDurationSeconds(S3_SESSION_REFRESH_TIME).build())
.build();
}
#Bean(name = "s3ClientGammaProd")
#Singleton
public AmazonS3 s3ClientGammaProd() {
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.valueOf(US_GAMMA_PROD_REGION))
.withCredentials(new STSAssumeRoleSessionCredentialsProvider
.Builder(S3BUCKET_ARN_PROD, NA_S3_SESSION_NAME)
.withRoleSessionDurationSeconds(S3_SESSION_REFRESH_TIME).build())
.build();
}
I don't think Mockito actually recognizes the #Qualifier annotation from Spring. The reason it worked before is that Mockito was doing property injection, as there was no other way to do it.
After changes, with the constructor in place, Mockito switched from property injection to constructor injection. What's important is that both s3ClientAlphaBeta and s3ClientGammaProd are of the same type (AmazonS3). Apparently, with constructor injection, Mockito isn't doing so well to differentiate the parameters by its name. Possible reason is that the names of the parameters aren't preserved after compilation, so there's no way for Mockito to match them. Instead, it uses the first mock (spy) it finds with a matching type (either s3ClientAlphaBeta or s3ClientGammaProd) and uses it for both parameters to construct an instance of S3Accessor.
To avoid your issue and gain more control over how the tested object is being created, I'd suggest abandoning usage of #InjectMocks and using the constructor directly in the tested class i.e. doing:
s3Accessor = new S3Accessor(s3ClientAlphaBeta, s3ClientGammaProd);
somewhere below MockitoAnnotations.openMocks(this).
You can find more information on how #InjectMocks annotation works here.

How do I use JUnit 5 with testcontainers in a custom network?

I can't seem to get the following the following scenario to work. I am trying to create a scenario
where two containers talked with each other on a separate network using JUnit 5 constructs.
#Testcontainers
class TestContainerTests {
#BeforeAll
static void setup() {
networkName = RandomStringUtils.randomAlphabetic(20);
network =
Network.builder()
.createNetworkCmdModifier(
createNetworkCmd -> {
createNetworkCmd.withName(networkName);
})
.build();
}
private static String networkName;
private static Network network;
#Container
private static GenericContainer<?> whoami =
new GenericContainer<>("containous/whoami")
.withNetwork(network)
.withNetworkAliases("whoami")
.withExposedPorts(80)
.waitingFor(Wait.forLogMessage(".*Starting up on port 80.*", 1));
/**
* An alpine container that is set to sleep. this is used for testing a specific scenario
*/
#Container
private GenericContainer<?> alpine =
new GenericContainer<>("alpine")
.withCommand("sleep 600")
.withNetwork(network);
#Test
void testWhoAmI() {
final var url =
UriComponentsBuilder.newInstance()
.scheme("http")
.host("localhost")
.port(whoami.getMappedPort(80))
.toUriString();
final var responseBody =
WebTestClient.bindToServer()
.baseUrl(url)
.build()
.get()
.uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.returnResult()
.getResponseBody();
assertThat(responseBody).contains("GET / HTTP/1.1");
}
#Test
void connection() throws Exception {
// this fails connection
final var wget = alpine.execInContainer("wget", "-qO-", "http://whoami/");
System.err.println(wget.getStderr());
assertThat(wget.getStdout()).contains("GET / HTTP/1.1");
}
}
I am aware I can simply manage the lifecycle myself using #BeforeAll and #AfterAll, but I am looking for a way to get it working with the existing annotations.
Using the default methods from Testcontainers for creating networks (outside of #BeforeAll lifecycle methods) should already work for you:
https://www.testcontainers.org/features/networking/#advanced-networking
#Testcontainers
class TestContainerTests {
private static Network network = Network.newNetwork();
// ...
}

How to mock FindPublisher in reactive mongo driver

I'm writing an application using mongo reactive driver and reactivestreams library in Java.
I have the following DAO code:
#Override
public Flux<ContentVersion> findBySearch(String appKey, ContentVersionSearchRequest request, Pager pager) {
final var searchResultsPublisher = mongoClient.getDatabase(appKey)
.getCollection(COLLECTION_CONTENT_VERSION, ContentVersion.class)
.find(prepareSearchFilter(request))
.sort(orderBy(ascending(FIELD_VERSION_STATUS_ORDER), descending(FIELD_UPDATE_DATE)))
.skip(pager.getSkip())
.limit(pager.getMax());
return Flux.from(searchResultsPublisher);
}
In junit tests, I mock MongoClient, MongoDatabase, MongoCollection but finally MongoCollection returns a FindPublisher and I do not know how to mock it correctly.
I have successfully written a unit test by mocking subscribe method as follows. However this does not seem right to me.
#Mock
private MongoClient mongoClient;
#Mock
private MongoDatabase database;
#Mock
private MongoCollection<ContentVersion> collection;
#Mock
private FindPublisher<ContentVersion> findPublisher;
#Mock
private UpdateResult updateResult;
#InjectMocks
private ContentVersionDaoImpl contentVersionDao;
#BeforeEach
void initCommonMocks() {
when(mongoClient.getDatabase("ddpApp")).thenReturn(database);
when(database.getCollection(MongoConstants.COLLECTION_CONTENT_VERSION, ContentVersion.class)).thenReturn(collection);
when(collection.find(any(Bson.class))).thenReturn(findPublisher);
when(collection.find(any(Document.class))).thenReturn(findPublisher);
when(findPublisher.limit(anyInt())).thenReturn(findPublisher);
when(findPublisher.skip(anyInt())).thenReturn(findPublisher);
when(findPublisher.sort(any())).thenReturn(findPublisher);
}
#Test
void shouldFindBySearch() {
final var contentVersion1 = new ContentVersion();
final var contentVersion2 = new ContentVersion();
final var testPublisher = TestPublisher.<ContentVersion>createCold()
.emit(contentVersion1, contentVersion2);
doAnswer(invocation -> {
testPublisher.subscribe(invocation.getArgument(0, Subscriber.class));
return null;
}).when(findPublisher).subscribe(any());
final var searchFlux = contentVersionDao
.findBySearch("ddpApp", new ContentVersionSearchRequest(null, null, null), new Pager(1, 10));
StepVerifier.create(searchFlux)
.expectNext(contentVersion1)
.expectNext(contentVersion2)
.expectComplete()
.verify();
}
Does anybody know an elegant way of writing a junit test to test fetching multiple documents from mongodb?

Using different mocks in different states Pact provider in Dropwizard

I'm trying to set up a pact provider with an application running Dropwizard but I'm having trouble using different mocks for different states. Is there an equivalent to Spring's #MockBean in Dropwizard or some other way to achieve this in Dropwizard?
This is my Provider class:
#RunWith(PactRunner.class)
#Provider("my-proxy")
#PactFolder("src/test/java/pact/pacts")
public class UserContractTest {
private static final String CONFIG_PATH = ResourceHelpers.resourceFilePath("config.yml");
// I want to do the commented out code in Dropwizard with Dropwizard equivalents
// #MockBean
// private MyClass myMockedClass;
#ClassRule
public static final DropwizardAppRule<MyAppConfiguration> RULE =
new DropwizardAppRule<>(MyAppMock.class, CONFIG_PATH);
#TestTarget
public final Target target = new HttpTarget(RULE.getLocalPort());
#State("default")
public void toDefaultState(Map<String, String> params) {
if(params.get("someState").equals("stateOne")) {
// when(myMockedClass.someFunc()).thenReturn("someAnswerForStateOne");
} else {
// when(myMockedClass.someFunc()).thenReturn("someAnswerForAllOtherStates");
}
}
}
I ended up using argThat on the request params to be able to use two different method mocks on the same method.
public class MyAppMock extends MyApp {
#Override
protected UserProvider getUserProvider(Config config) {
var myServiceMock = mock(UserProvider.class);
var userResponseType = new UserResponseType();
var user = new User();
user.setName("Lisa");
user.setAge(30);
userResponseType.setUser(user);
doReturn(userResponseType)
.when(myServiceMock)
.createUser(argThat(createUserRequestType -> createUserRequestType.getName().equals("Lisa")), any());
var errorMessage = "Error";
doThrow(new SomeException(errorMessage, SC_NOT_FOUND))
.when(myServiceMock)
.createPaymentOrder(argThat(createUserRequestType -> createUserRequestType.getName().equals("John")), any());
return myServiceMock;
}
}

How to load the HSQL DB once before running all junit in Java project

My project is not Spring based .Its a java with Hibernate.Building tool - Maven.
I am loading data from one database to HSQL DB before running junits.
My DB util class:
public class DatabaseUtil {
SchemaLoad schemaLoad = new SchemaLoad();
DataLoad dataLoad = new DataLoad();
boolean dataLoaded = false;
static final String filename1 = "test1.txt";
static final String filename2 = "text2.txt";
void dbLoad() throws SQLException {
if (!dataLoaded) {
schemaLoad.cloneSchema(filename1);
dataLoad.exportData(filename2);
System.out.println("***********executed**********8");
dataLoaded = true;
}
}
}
First Test Case:
public class TestCase {
TrainRepository trainRepository = new TrainRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForTrainRepo() throws Exception {
//TestCases
}
Second Test case:
public class TestCase1 {
AirRepository airRepository = new AirRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForAirRepo() throws Exception {
//TestCases
}
Both the test cases are running fine.But Its executing databaseUtil.dbLoad(); method on each junit.
My question is I need to load the database only once ie before start of first junit and need to set some indicator .The further junits need to check the DB instance If DB instance is there it should not load the data ie DatabaseUtil class need to be singleton.
All the junits are running through maven suffire plugin during mvn install phase.
Kindly help me to achieve this.
void dbLoad() will be called each time.
then use a static variable to keep track
static boolean dataLoaded = false;
if you don't use spring you need to implement caching yourself. you have a few option. use static field with some kind of synchronization (in case you use/plan to use threads). other option is to switch to testng that gives you #BeforeGroup functionality so you can mark all your db tests and have your initialization run before.

Categories