Mock whenever new instance created without PowerMockito JUnit5 - java

JUnit5 does not support PowerMockRunner hence the following code will not work whenever you migrate from JUnit4 to JUnit5.
Eg.
Code you trying to inject mock
import javax.naming.InvalidNameException;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.publish();
}
public void publish() {
try {
Sample s = new Sample();
s.invoke("Hello");
} catch (InvalidNameException e) {
throw new ServiceFailureException(e.getMessage());
}
}
}
Here you are trying to test publish method where you mock the Sample instance to respond with different responses.
In JUnit4 you could have use PowerMockito to achieve that.
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.naming.InvalidNameException;
#RunWith(PowerMockRunner.class)
#PrepareForTest({Main.class})
public class MainTest {
#Test
public void testPublishSuccess() {
Main m = new Main();
Assert.assertEquals("Expected result not found", "success", m.publish());
}
#Test
public void testPublishFailure() throws Exception{
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenReturn("failure");
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
Assert.assertEquals("Expected result not found", "failure", m.publish());
}
#Test(expected = ServiceFailureException.class)
public void testPublishException() throws Exception{
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenThrow(new InvalidNameException("Invalid name provided"));
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
m.publish();
}
}
With the introduction of JUnit5, the test cases are failing at mock creating new instances because PowerMockRunner does not support JUnit5.
What is the alternate for using PowerMockito with JUnit5.

As PowerMockito does not support JUnit5, we can use Mockito inline. Here is the code which replace the PowerMockito.
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import javax.naming.InvalidNameException;
public class MainTestJunit5 {
#Test
public void testPublishSuccess() {
Main m = new Main();
Assertions.assertEquals("success", m.publish(), "Expected result not found");
}
#Test
public void testPublishFailure() throws Exception{
try (MockedConstruction<Sample> mockedConstruction = Mockito.mockConstruction(Sample.class, (sampleMock, context) -> {
Mockito.when(sampleMock.invoke(Mockito.anyString())).thenReturn("failure");
})) {
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenReturn("failure");
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
Assertions.assertEquals("Expected result not found", "failure", m.publish());
}
}
#Test
public void testPublishException() throws Exception{
try (MockedConstruction<Sample> mockedConstruction = Mockito.mockConstruction(Sample.class, (sampleMock, context) -> {
Mockito.when(sampleMock.invoke(Mockito.anyString())).thenThrow(new InvalidNameException("Invalid name found"));
})){
Main m = new Main();
boolean error = false;
try {
m.publish();
} catch (ServiceFailureException e) {
error = true;
}
Assertions.assertTrue(error, "Exception throwing expected");
}
}
}
Couple of things you need to pay attention
Setting up mockito-inline need additional dependency and an additional configuration.
Extra test runners (PowerMockRunner) and preparation for testing is not needed.
MockedConstruction is scoped, so you have to put all the mocking and processing done within that code block.
JUnit5 messages are the final method argument.
Mockito documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#49

Related

trouble with instantiating variables with #Before and #BeforeClass with JUnit

I am testing out new technologies and generated a HelloWorld template using AWS SAM.
I wrote some simple unit tests for my simple app but I am having trouble using #Before and #BeforeClass. If I move the contents of the initialize method inside each of my tests, then they pass but I get all NullPointerExceptions when I try to do it separately.
Originally I decided to use #Before and my class was like so-
package helloworld;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class AppTest {
App app;
APIGatewayProxyResponseEvent result;
String content;
#Before
public void initialize(){
App app = new App();
APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
System.out.println(result);
String content = result.getBody();
System.out.println(content);
}
#Test
public void successfulResponseCode(){
System.out.println("1 " + content);
assertEquals(result.getStatusCode().intValue(), 200);
}
#Test
public void successfulResponseString() {
System.out.println("2 " + content);
assertNotNull(content);
assertTrue(content.contains("\"message\""));
assertTrue(content.contains("\"hello world\""));
assertTrue(content.contains("\"location\""));
}
#Test
public void readsDatabase(){
System.out.println("3 " + content);
assertTrue(content.contains("Devon"));
assertTrue(content.contains("Luana"));
}
#Test
public void headersCreated(){
System.out.println("4 " + content);
assertEquals(result.getHeaders().get("Content-Type"), "application/json");
assertTrue(result.getHeaders().get("Access-Control-Allow-Origin").contains("*"));
assertEquals(result.getHeaders().get("Access-Control-Allow-Methods"), "*");
}
}
The system logs within the initialize class were outputting correctly but inside each #Test they were null. For some reason the instantiated variables were not making it within the scope of the test.
I am a bit of novice at tests and I realized I should be using #BeforeClass anyways, since I didn't need to make a new call for each separate test and I hoped that possibly that might fix my problem. It still didn't work so then I changed the variables to static but that also didn't help. I'm sure the error must be simple but I can't figure out what it is.
I see a few other people were having the same problem here- Junit #Before not working properly and I tried changing my variables to private but still no luck.
I also saw this - Java JUnit testing not working with #Before annotation but I am using junit 4.13.1 although like the author, my tests work if I simply initialize outside the #Before annotation like so -
package helloworld;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class AppTest {
App app = new App();
APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
String content = result.getBody();
// #BeforeClass
// public static void initialize(){
// App app = new App();
// APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
// System.out.println(result);
// String content = result.getBody();
// System.out.println(content);
// }
#Test
public void successfulResponseCode(){
System.out.println("1 " + content);
assertEquals(result.getStatusCode().intValue(), 200);
}
#Test
public void successfulResponseString() {
System.out.println("2 " + content);
assertNotNull(content);
assertTrue(content.contains("\"message\""));
assertTrue(content.contains("\"hello world\""));
assertTrue(content.contains("\"location\""));
}
#Test
public void readsDatabase(){
System.out.println("3 " + content);
assertTrue(content.contains("Devon"));
assertTrue(content.contains("Luana"));
}
#Test
public void headersCreated(){
assertEquals(result.getHeaders().get("Content-Type"), "application/json");
assertTrue(result.getHeaders().get("Access-Control-Allow-Origin").contains("*"));
assertEquals(result.getHeaders().get("Access-Control-Allow-Methods"), "*");
}
}

How to Execute AfterMehtod for only one Test method

I have to Test methods in my code and I wan to execute AfterMehtod for only one. Anybody have any idea how do this?
Here is My code:
package Demo;
import java.util.concurrent.TimeUnit;
import library.Utility;
import org.openqa.selenium.By;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import Pages.custom_actions_page;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
public class Custom_Actions extends start {
ExtentReports report;
ExtentTest logger;
String driverPath = "D:\\geckodriver-v0.16.1-win64\\geckodriver.exe";
#Test()
public void signin() throws Exception {
// To Locate the Username field
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
driver.findElement(By.id("username")).sendKeys("admin");
// To locate the Password field
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
driver.findElement(By.id("password")).sendKeys("admin123");
// Click on Login button
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
driver.findElement(By.id("submit")).click();
}
#Test(dependsOnMethods = { "signin" })
public void create_custom_action() {
report = new ExtentReports("D:/Reports/Report.html");
logger = report.startTest("Create Custom Action");
new custom_actions_page(driver).submit();
new custom_actions_page(driver).admin();
new custom_actions_page(driver).custom_ac();
new custom_actions_page(driver).createnew();
new custom_actions_page(driver).nameAs("fortesting").descriptionAs(
"description");
new custom_actions_page(driver).category();
new custom_actions_page(driver).assetsubtype();
new custom_actions_page(driver).assettype();
new custom_actions_page(driver).flintnameAs("hello:example.rb");
new custom_actions_page(driver).submit_butto();
new custom_actions_page(driver).Save_Button();
logger.log(LogStatus.PASS, "Custom Action Created Successfully");
}
#AfterMethod()
public void tearDown(ITestResult result) {
// Here will compare if test is failing then only it will enter into if
// condition
if (ITestResult.FAILURE == result.getStatus()) {
try {
Utility.captureScreenshot(driver, "CustomActionFail.png");
} catch (Exception e) {
System.out.println("Exception while taking screenshot "
+ e.getMessage());
}
}
report.endTest(logger);
report.flush();
driver.close();
}}
#AfterMethod is designed to make your life easier when you need to execute a the same block of code after each test without having to duplicate it in each test. So if you need it to execute only after one test, just embed it into that #Test method and remove the #AfterMethod annotated method.
There are a couple of ways of doing it using what is called as Native Injection within TestNG. For more details on what is the possible combinations for native injection in TestNG please refer here.
The simplest way would be to inspect the name of the #Test method that is about to be executed, from within your #AfterMethod annotated method, and if it matches the name of the method for which you need special execution, you proceed further, else you skip executing the #AfterMethod.
But this is a primitive approach, because if you refactor your test method's name to something else, your approach gets broken.
The other approach is to basically work with a marker interface and do the same logic as described above
Here's a sample that shows all this in action.
The marker annotation would look like below
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target(java.lang.annotation.ElementType.METHOD)
public #interface NeedsSpecialSetup {
}
Now your test class would look like below
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
public class SampleTestClass {
#NeedsSpecialSetup
#Test
public void testMethod1() {
System.err.println("Executing testMethod1()");
}
#Test
public void testMethod2() {
System.err.println("Executing testMethod2()");
}
#AfterMethod
public void afterMethod(Method method) {
NeedsSpecialSetup needsSpecialSetup = method.getAnnotation(NeedsSpecialSetup.class);
if (needsSpecialSetup == null) {
//Don't execute this setup because the method doesn't have the
//special setup annotation.
return;
}
System.err.println("Running special setup for " + method.getName() + "()");
}
}
Notice how we have added the annotation #NeedsSpecialSetup only for the method testMethod1() to indicate that we need the #AfterMethod to be executed only for testMethod1().
Here's the output
Executing testMethod1()
Running special setup for testMethod1()
Executing testMethod2()
===============================================
Default Suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================

How do I add retry logic to leanft with Junit tests?

While LeanFT uses JUnit for its test runner, it doesn't appear to implement 'TestRule'. This excludes a 'standard' method describe elsewhere.
How to Re-run failed JUnit tests immediately?
Anyone have a solution to this?
It seems that you refer to the fact that the report doesn't have the test result.
Indeed it seems it's no longer automatically reported when using TestRule.
However, you can manually report whatever you want to report.
Here's an example of Junit test that reports what we want it to report.
import com.hp.lft.report.CaptureLevel;
import com.hp.lft.report.ReportLevel;
import com.hp.lft.report.Reporter;
import com.hp.lft.sdk.web.Browser;
import com.hp.lft.sdk.web.BrowserFactory;
import com.hp.lft.sdk.web.BrowserType;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.*;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import unittesting.UnitTestClassBase;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
public class RetryTest extends UnitTestClassBase {
#BeforeClass
public static void setUpBeforeClass() throws Exception {
instance = new LeanFtTest();
globalSetup(LeanFtTest.class);
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
globalTearDown();
}
public class Retry implements TestRule {
private int retryCount;
public Retry(int retryCount) {
this.retryCount = retryCount;
}
public Statement apply(Statement base, Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Throwable caughtThrowable = null;
// implement retry logic here
for (int i = 0; i < retryCount; i++) {
try {
base.evaluate();
return;
} catch (Throwable t) {
caughtThrowable = t;
System.err.println(description.getDisplayName() + ": run " + (i+1) + " failed");
}
}
System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures");
throw caughtThrowable;
}
};
}
}
#Rule
public Retry retry = new Retry(3);
#Test
public void test2() throws Exception{
Reporter.startReportingContext("Reporting for test2");
Reporter.reportEvent("Reporting", "Reporting stuff", Status.Passed);
Reporter.reportEvent("Reporting", "Reporting some more stuff", Status.Failed);
Reporter.endReportingContext();
Object o = null;
o.equals("foo");
}
}
And this is how it looks like:

NullPointerException in while doing mockito unit test

I am new to mockito Junit testing. This one is my main class which I want to test:
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
#Component
public class IlinqChecksumCalculator {
private static Logger DATA_LOADER_CHECKSUM_CALCULATOR_LOGGER = Logger.getLogger(IlinqChecksumCalculator.class);
public String calculateCheckSum(String rfsdata) throws IOException {
System.out.println(rfsdata);
String checkSumValue = null;
if (StringUtils.isNotBlank(rfsdata)) {
try {
// Create MessageDigest object for MD5
MessageDigest digest = MessageDigest.getInstance("MD5");
// Update input string in message digest
digest.update(rfsdata.getBytes(), 0, rfsdata.getBytes().length);
// Converts message digest value in base 16 (hex)
checkSumValue = new BigInteger(1, digest.digest()).toString(16);
} catch (NoSuchAlgorithmException exception) {
DATA_LOADER_CHECKSUM_CALCULATOR_LOGGER.error(
"Error in determineInputCheckSum() method during calculation of checksum for Input JSON String for ",
exception);
}
}
System.out.println("Final checksum value is:" + checkSumValue);
return checkSumValue;
}
}
This one is my test class:
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class IlinqChecksumCalculatorTest {
private IlinqChecksumCalculator ilinqCheckSum;
#Before
public void setUp() throws Throwable {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCheckSum() throws IOException {
when(ilinqCheckSum.calculateCheckSum("abcde")).thenReturn("defgh");
assertEquals("defgh", ilinqCheckSum.calculateCheckSum("abcde"));
}
}
I am getting a null pointer exception.
Just to answer your question: to handle ilinqCheckSum as mock, you shouuld annotate it with #Mock.
But here you should not use mockito! You want to test IlinqChecksumCalculator and not a mock! You should create a real instance of it and inject the dependendencies as mock if necessary.
By mocking calculateCheckSum method you are not covering any code in your unit test. I think you should not use Mock here. Try below test method.
public void testCheckSum() throws IOException {
String result = ilinqCheckSum.calculateCheckSum("abcde")
assertNotNull(result );
}

Can't verify mock method call from RxJava Subscriber

I'm trying to unit test presenter in my Android app. Method I'm trying to test looks like this:
#Override
public boolean loadNextPage() {
if (!mIsLoading) {
mIsLoading = true;
if (mViewReference.get() != null) {
mViewReference.get().showProgress();
}
mService.search(mSearchQuery, ++mCurrentPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(itemsPage -> {
mIsLoading = false;
mTotalPages = itemsPage.getPagination().getTotalPages();
if (mViewReference.get() != null) {
mViewReference.get().showMovies(itemsPage.getItems());
}
},
error -> {
mIsLoading = false;
Log.d(LOG_TAG, error.toString());
});
}
return mTotalPages == 0 || mCurrentPage < mTotalPages;
}
mService is Retrofit interface and mService.search() method returns RxJava's Observable<SearchResults>. My unit test code looks like this:
package mobi.zona.presenters;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import com.example.api.Service;
import com.example.model.Movie;
import com.example.model.SearchResults;
import com.example.views.MoviesListView;
import rx.Observable;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class SearchPresenterTest {
#Mock
Service mService;
#Mock
MoviesListView mMoviesListView;
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery);
presenter.loadNextPage();
verify(mService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
}
The problem is the third verify(mMoviesListView, times(1)).showMovies(movies); line - it allways fails. Whem I'm trying to debug this test I see that control flow never goes into .subscribe(itemPage - {.... I think that it's something related to the fact that I'm subscribing on Schedulers.io() thread, but have no idea on how to fix this. Any ideas?
EDIT 1:
Changed the presenter to take Scheduler's as constructor parameters. Changed test to look like this:
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mZonaService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery,
Schedulers.test(), Schedulers.test());
presenter.loadNextPage();
verify(mZonaService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
Still getting this test failure message:
Wanted but not invoked:
mMoviesListView.showMovies([]);
-> at com.example.presenters.SearchPresenterTest.testLoadNextPage(SearchPresenterTest.java:46)
However, there were other interactions with this mock:
mMoviesListView.showProgress();
-> at com.example.presenters.SearchPresenter.loadNextPage(SearchPresenter.java:41)
In my apps interactors/use-cases/model (mService in your case) is responsible for specifying Scheduler for the operation (since it knows better what kind of operation it does).
So, move your subscribeOn to mService. After that your mock will work fine.
Going deeper, if now you'll want to test mService I would recommend you to make it "dependent" on Scheduler. In other words - add Sheduler as a constructor parameter.
public class MyService {
private final Scheduler taskScheduler;
public MyService(Scheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
// ...
public Observable<Something> query() {
return someObservable.subscribeOn(taskScheduler);
}
}
Then, in tests you can use Schedulers.immediate() and for actual app Schedulers.io() (or whatever you like, really).

Categories

Resources