These libraries are loaded:
JUnit 5.3.2
JaCoCo 0.8.2
Mockito 2.10.0
Only element "static {...}" appears with 100% coverage. All the rest is at 0%:
The unit test class has annotations #ExtendWith(SpringExtension.class) and #AutoConfigureMockMvc. The service is injected with #Mock.
doReturn(actual).when(service).get(param);
when(service.get(param)).thenReturn(actual);
expected = service.get(param);
verify(service, times(1)).get(param);
assertEquals(expected, actual);
assertEquals(actual, expected);
My ServiceImpl class is red when I click any method. It extends an abstract class. Jackson's ObjectMapper is red, and also the entire lines within the methods. For example:
public CustomReturnObject get(final CustomParamObject paramObject) {
try {
return retryTemplate.execute(status -> {
String json = repository.get(paramObject);
CustomReturnObject returnObject = json2CustomObject(json, paramObject);
if (returnObject == null) {
returnObject = new CustomReturnObject();
returnObject.setId(paramObject.getId());
}
return returnObject;
});
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
return null;
}
}
Similarly to https://stackoverflow.com/a/46614216/244993 let's put aside Spring, because there is IMO clearly something wrong with your expectations/understanding about core thing here - mocking.
By
doReturn(actual).when(service).get(param);
expected = service.get(param);
verify(service, times(1)).get(param);
assertEquals(expected, actual);
you are not testing get method, you are testing something that always returns actual, no matter what is actually written in get, because in this case it is not executed.
Here is complete example as a proof:
src/main/java/hello/GreetingService.java:
package hello;
class GreetingService {
Object get(Object param) {
throw new UnsupportedOperationException();
}
}
src/test/java/hello/GreetingServiceTest.java:
package hello;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
#ExtendWith(MockitoExtension.class)
public class GreetingServiceTest {
#Mock
public GreetingService service;
#Test
void test() {
Object param = new Object();
Object actual = new Object();
doReturn(actual).when(service).get(param);
Object expected = service.get(param);
verify(service, Mockito.times(1)).get(param);
assertEquals(expected, actual);
}
}
build.gradle :
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.mockito:mockito-junit-jupiter:2.23.4'
}
Real method get throws UnsupportedOperationException, however above test succeeds, so real method was not executed. As another way to proof that get not executed: put a breakpoint into it and execute test in debug mode from IDE - breakpoint won't be reached.
Coverage shows what was really executed and hence absolutely correct that it is zero for methods that are not executed.
Related
I'm trying to write junit for the following try-catch block to improve coverage of the code. I've got the test to cover try block, but how do do it for catch block?
Following is the code with try-catch block,
public boolean testDb() {
boolean dbHealth = true;
try {
Session session = sessionFactory.getCurrentSession();
SQLQuery sqlQuery = session.createSQLQuery("SELECT empId from employee");
sqlQuery.executeUpdate();
} catch (Exception e) {
dbHealth = false;
LOG.error(e);
}
return dbHealth;
}
This is what I tried for the coverage of catch block, but still 'try' block is getting covered instead of 'catch' block
#Test
public void testDb_throwException() {
SessionFactory sessionFactory = mock(SessionFactory.class);
Session session= mock(Session.class);
Query query = mock(Query.class);
when(sessionFactory.getCurrentSession()).thenReturn(session);
when(sessionFactory.openSession()).thenReturn(session);
when(mockSession.createSQLQuery(Mockito.anyString())).thenReturn(query);
when(query.executeUpdate()).thenThrow(new RuntimeException("sql exception"));
boolean res= baseDaoImpl.testDatabaseHealth();
Assert.assertTrue(res);
}
There's a few things going on here.
First, All of the mocks that you're creating in this test aren't being injected into the service under test, so they aren't doing squat.
Second, you're returning a mock from the sessionfactory named, "session", but your defining behavior on a mock named, "mockSession", see these two lines:
when(sessionFactory.openSession()).thenReturn(session);
when(mockSession.createSQLQuery(Mockito.anyString())).thenReturn(query);
Third, I suspect that your test class already has a configured baseDaoImpl, with mocks injected into it, otherwise it would be throwing NPEs in a few places. What you want to do is configure on those mocks. You'll want to use reset on the global SessionFactory mock if you're going to be using it to return other mocked instances in other tests.
Here's a full test class with what I believe your baseDaoImpl to look like. It includes all the imports that I know of, I don't know which Session, SessionFactory, or SQLQuery class you're using.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#ExtendWith(MockitoExtension.class)
class BaseDaoImplTest {
#Mock // mock the session factory
SessionFactory sessionFactory;
#InjectMocks // inject the mock into the baseDao
BaseDaoImpl baseDao;
#Test
void somethingToTest() {
// define query mock to throw exception
SQLQuery sqlQuery = mock(SQLQuery.class); // your class actually returns an SQLQuery, so you need to mock this and not the interface
when(sqlQuery.executeUpdate()).thenThrow(new RuntimeException("sql exception"));
// define session mock to return the sqlQuery mock created above
Session session = mock(Session.class);
when(session.createSQLQuery(anyString())).thenReturn(sqlQuery);
// instruct the session factory mock that's injected into your class under test to return the session created above
when(sessionFactory.getCurrentSession()).thenReturn(session);
assertFalse(baseDao.somethingToTest());
}
#Test
void somethingToTest_condensedVersion() {
// since all your want to test is that the catch block behaves properly,
// instruct the session factory mock to throw an exception
when(sessionFactory.getCurrentSession()).thenThrow(new RuntimeException());
assertFalse(baseDao.somethingToTest());
}
}
I have tried to configure an existing Maven project to run using cucumber-junit-platform-engine.
I have used this repo as inspiration.
I added the Maven dependencies needed, as in the linked project using spring-boot-starter-parent version 2.4.5 and cucumber-jvm version 6.10.4.
I set the junit-platform properties as follows:
cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=fixed
cucumber.execution.parallel.config.fixed.parallelism=4
Used annotation #Cucumber in the runner class and #SpringBootTest for classes with steps definition.
It seems to work fine with creating parallel threads, but the problem is it creates all the threads at the start and opens as many browser windows (drivers) as the number of scenarios (e.g. 51 instead of 4).
I am using a CucumberHooks class to add logic before and after scenarios and I'm guessing it interferes with the runner because of the annotations I'm using:
import java.util.List;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.EventHandler;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.TestRunFinished;
import io.cucumber.plugin.event.TestRunStarted;
import io.github.bonigarcia.wdm.WebDriverManager;
public class CucumberHooks implements ConcurrentEventListener {
#Autowired
private ScenarioContext scenarioContext;
#Before
public void beforeScenario(Scenario scenario) {
scenarioContext.getNewDriverInstance();
scenarioContext.setScenario(scenario);
LOGGER.info("Driver initialized for scenario - {}", scenario.getName());
....
<some business logic here>
....
}
#After
public void afterScenario() {
Scenario scenario = scenarioContext.getScenario();
WebDriver driver = scenarioContext.getDriver();
takeErrorScreenshot(scenario, driver);
LOGGER.info("Driver will close for scenario - {}", scenario.getName());
driver.quit();
}
private void takeErrorScreenshot(Scenario scenario, WebDriver driver) {
if (scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", "Failure");
}
}
#Override
public void setEventPublisher(EventPublisher eventPublisher) {
eventPublisher.registerHandlerFor(TestRunStarted.class, beforeAll);
}
private EventHandler<TestRunStarted> beforeAll = event -> {
// something that needs doing before everything
.....<some business logic here>....
WebDriverManager.getInstance(DriverManagerType.CHROME).setup();
};
}
I tried replacing the #Before tag from io.cucumber.java with the #BeforeEach from org.junit.jupiter.api and it does not work.
How can I solve this issue?
New answer, JUnit 5 has been improved somewhat.
If you are on Java 9+ you can use the following in junit-platform.properties to enable a custom parallelism.
cucumber.execution.parallel.enabled=true
cucumber.execution.parallel.config.strategy=custom
cucumber.execution.parallel.config.custom.class=com.example.MyCustomParallelStrategy
And you'd implement MyCustomParallelStrategy as:
package com.example;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfiguration;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationStrategy;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Predicate;
public class MyCustomParallelStrategy implements ParallelExecutionConfiguration, ParallelExecutionConfigurationStrategy {
private static final int FIXED_PARALLELISM = 4
#Override
public ParallelExecutionConfiguration createConfiguration(final ConfigurationParameters configurationParameters) {
return this;
}
#Override
public Predicate<? super ForkJoinPool> getSaturatePredicate() {
return (ForkJoinPool p) -> true;
}
#Override
public int getParallelism() {
return FIXED_PARALLELISM;
}
#Override
public int getMinimumRunnable() {
return FIXED_PARALLELISM;
}
#Override
public int getMaxPoolSize() {
return FIXED_PARALLELISM;
}
#Override
public int getCorePoolSize() {
return FIXED_PARALLELISM;
}
#Override
public int getKeepAliveSeconds() {
return 30;
}
On Java 9+ this will limit the max-pool size of the underlying forkjoin pool to FIXED_PARALLELISM and there should never be more then 8 web drivers active at the same time.
Also once JUnit5/#3044 is merged, released an integrated into Cucumber, you can use the cucumber.execution.parallel.config.fixed.max-pool-size on Java 9+ to limit the maximum number of concurrent tests.
So as it turns out parallism is mostly a suggestion. Cucumber uses JUnit5s ForkJoinPoolHierarchicalTestExecutorService which constructs a ForkJoinPool.
From the docs on ForkJoinPool:
For applications that require separate or custom pools, a ForkJoinPool may be constructed with a given target parallelism level; by default, equal to the number of available processors. The pool attempts to maintain enough active (or available) threads by dynamically adding, suspending, or resuming internal worker threads, even if some tasks are stalled waiting to join others. However, no such adjustments are guaranteed in the face of blocked I/O or other unmanaged synchronization.
So within a ForkJoinPool when ever a thread blocks for example because it starts asynchronous communication with the web driver another thread may be started to maintain the parallelism.
Since all threads wait, more threads are added to the pool and more web drivers are started.
This means that rather then relying on the ForkJoinPool to limit the number of webdrivers you have to do this yourself. You can use a library like Apache Commons Pool or implement a rudimentary pool using a counting semaphore.
#Component
#ScenarioScope
public class ScenarioContext {
private static final int MAX_CONCURRENT_WEB_DRIVERS = 1;
private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_WEB_DRIVERS, true);
private WebDriver driver;
public WebDriver getDriver() {
if (driver != null) {
return driver;
}
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
driver = CustomChromeDriver.getInstance();
} catch (Throwable t){
semaphore.release();
throw t;
}
return driver;
}
public void retireDriver() {
if (driver == null) {
return;
}
try {
driver.quit();
} finally {
driver = null;
semaphore.release();
}
}
}
I have been able to convert message consumer pact tests to junit5, but am not sure how to use the information in the junit5 provider readme to convert the corresponding message provider verification tests. Can someone point to an example or suggest an outline of how the provider tests for message queue providers are supposed to work with the PactVerificationcontext?
I am trying to convert something like:
import au.com.dius.pact.provider.PactVerifyProvider;
import au.com.dius.pact.provider.junit.Consumer;
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.target.AmqpTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
#RunWith(PactRunner.class)
#Provider("provider")
#Consumer("consumer")
#PactFolder("target/pacts")
public class MessageProviderPact {
#TestTarget
public final Target target = new AmqpTarget();
private KafkaTemplate<String, MsgObject> kafkaTemplate
= (KafkaTemplate<String, MsgObject>)Mockito.mock(KafkaTemplate.class);
private MessageProducer messageProducer = new MessageProducer(kafkaTemplate);
#Test
#PactVerifyProvider("case a")
public String verifyCaseA() throws IOException {
// given
ListenableFuture<SendResult<String, MsgObject>> future =
mock(ListenableFuture.class);
doReturn(future).when(kafkaTemplate).send(any(String.class),
any(MsgObject.class));
// when
DomainObj domainObj = new DomainObj();
String topic = "kafka_add";
messageProducer.send(topic, domainObj);
// then
ArgumentCaptor<MsgObject> messageCapture = ArgumentCaptor.forClass(
MsgObject.class);
verify(kafkaTemplate, times(1)).send(eq(topic),
messageCapture.capture());
// returning the message
return objectMapper.writeValueAsString(messageCapture.getValue());
}
}
You should not use the kafka template to verify the Pact message, you might have created the test Object for unit testing in order to test the Messages you can use the same test Objects. You can find the full implementation here.
An example can be found in the pact-jvm project repo
The relevant code has been included below:
#Provider("AmqpProvider")
#PactFolder("src/test/resources/amqp_pacts")
public class AmqpContractTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpContractTest.class);
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void testTemplate(Pact pact, Interaction interaction, PactVerificationContext context) {
LOGGER.info("testTemplate called: " + pact.getProvider().getName() + ", " + interaction);
context.verifyInteraction();
}
#BeforeEach
void before(PactVerificationContext context) {
context.setTarget(new MessageTestTarget());
}
#State("SomeProviderState")
public void someProviderState() {
LOGGER.info("SomeProviderState callback");
}
#PactVerifyProvider("a test message")
public String verifyMessageForOrder() {
return "{\"testParam1\": \"value1\",\"testParam2\": \"value2\"}";
}
}
I am trying to include Room persistence library to my android project. I am using gradle dependency android.arch.persistence.room:runtime:1.1.1 library, not the androidx
I have tried changing the version to 1.1.0 & 1.1.0-beta2 but getting same error.
Gradle dependencies for Room:
// Room components
implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion"
Here $rootProject.roomVersion value is 1.1.1
Support library version:
`implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'`
The AppDatabase class:
import android.arch.persistence.room.Database;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.content.Context;
import com.samcaraschools.userlistapp.module.User;
#Database(entities = {User.class},version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public static AppDatabase mDatabase;
public abstract UserDao userDao();
public static AppDatabase getAppDatabase(Context context) {
if (mDatabase == null) {
synchronized (AppDatabase.class) {
if (mDatabase == null) {
mDatabase = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "word_database")
// Wipes and rebuilds instead of migrating
// if no Migration object.
// Migration is not part of this practical.
.fallbackToDestructiveMigration()
.build();
}
}
}
return mDatabase;
}
}
With the above code in place I am getting the following error:
error: incompatible types: SupportSQLiteDatabase cannot be converted to AppDatabase
What is wrong with my code?
The error is because of the name of the object of class AppDatabase. Rename it to any object name than mDatabase, then it will function properly.
If you check your AppDatabase_Impl.java, you will notice that mDatabase has error. This is because SupportSQLiteDatabase.java has object name mDatabase which gets override by the object name placed in AppDatabase.
I took a working test written for JUnit using Mockito and tried to adapt it to work with TestNG but oddly using TestNG only one test will work.
I think it is somehow related to the resetting of the mocks but I have played around with trying to call Mockito.reset and using BeforeMethod and BeforeClass and different combinations but still can only get one test to pass.
What I need to do to get the test to work?
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(calculatorController).build();
}
#AfterMethod
public void reset() {
Mockito.reset(calculatorService);
}
#Test
public void addFunctionTest() throws Exception {
Assert.assertNotNull(calculatorController);
Result expectedResult = new Result();
expectedResult.setResult(10);
when(calculatorService.add(anyInt(), anyInt())).thenReturn(expectedResult);
mockMvc.perform(get("/calculator/add").accept(MediaType.APPLICATION_JSON_VALUE)
.param("val1", "100")
.param("val2", "100"))
.andExpect(content().contentType("application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.result", equalTo(10)));
verify(calculatorService, times(1)).add(anyInt(), anyInt());
}
#Test
public void subtractFunctionTest() throws Exception {
Assert.assertNotNull(calculatorController);
Result expectedResult = new Result();
expectedResult.setResult(90);
when(calculatorService.subtract(anyInt(), anyInt())).thenReturn(expectedResult);
mockMvc.perform(get("/calculator/subtract").accept(MediaType.APPLICATION_JSON_VALUE)
.param("val1", "100")
.param("val2", "10"))
.andExpect(content().contentType("application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.result", equalTo(90)));
verify(calculatorService, times(1)).subtract(anyInt(), anyInt());
}
The second test always seems to fail on assertions that either content type is not set or the expected result is wrong.
It seems like the response for the first test is somehow being evaluated in the second test and so is obviously wrong!
I know the controller and service work as expected and the exact same tests running with jUnit actually work ok.
I have managed to get the tests to perform properly only when I do the following:
#BeforeGroups("subtract")
public void reset() {
Mockito.reset(calculatorService);
mockMvc = MockMvcBuilders.standaloneSetup(calculatorController).build();
}
#Test(groups = "subtract")
public void subtractFunctionTest() throws Exception {
System.out.println("***** IN METHOD *****");
Assert.assertNotNull(calculatorController);
Result expectedResult = new Result();
expectedResult.setResult(90);
when(calculatorService.subtract(anyInt(), anyInt())).thenReturn(expectedResult);
//Perform HTTP Get for the homepage
mockMvc.perform(get("/calculator/subtract").accept(MediaType.APPLICATION_JSON_VALUE)
.param("val1", "100")
.param("val2", "10"))
.andExpect(content().contentType("application/json"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.result", equalTo(90)));
//Verify that the service method was only called one time
verify(calculatorService, times(1)).subtract(anyInt(), anyInt());
}
This means I need to add one of these reset methods for each test method though and I then need a group per test method which doesnt seem correct.
There is a difference in the behaviour of these frameworks:
JUnit creates a new instance of class for every of its test methods. This means that the fields are not shared between tests.
But TestNG creates only one object and thus the state in fields is shared between to #Tests
For Mockito you need to init mocks before every test method so that the state is not shared between two #Tests in TestNG:
#BeforeMethod
public void init() {
MockitoAnnotations.initMocks(this);
}
For JUnit it works out of box because 2nd #Test has its own fields and its own mocks.
I'm not sure, if this answer fits to the questioners problem, because the mocks are not listed. But I'd like to re-state one comment from andy in the accepted answer, which helped me out on the same problem. Here are some more details and an example:
public class B {
private A a;
B (A a) {
this.a = a
}
// ...
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MyTest {
#Mock
A a;
#InjectMocks
B B;
#BeforeMethod
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
void test1() {
// ...
// b.toString();
// b.getA().toString();
// a.toString();
}
#Test
void test2() {
// ...
// b.toString();
// b.getA().toString();
// a.toString();
}
#AfterMethod
public void reset()
{
// Solution:
b = null;
}
}
Mockitos MockitoAnnotations.initMocks(this) only re-initializes mocks, as Mockito.reset(a) only resets mocks. The problem is the class under test, which is annotated with #InjectMocks. This class, here named B, is not initialized again. It is initialized for the first test with a mock of A, the mock of A is re-initialized but B still contains the first version of A.
The solution is to reset the class under test manually with b = null at any plausible place (#AfterMethod or before initMocks). Then Mockito also re-inizialized the class under test B.
You can also use MockitoTestNGListener it is similar to JUnit MockitoJUnitRunner or MockitoExtension.
Example of code:
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import java.util.Map;
#Listeners(MockitoTestNGListener.class)
public class MyTest {
#Mock
Map map;
#InjectMocks
SomeType someType;
#Test
void test() {
// ...
}
}