How can I unit test the ProcessorBean?
Since I only wan't to test the ProcessorBean and not the Dao, I need to stub or mock the Dao, but I have no idea how I could do that with Junit.
I'm using Junit4 and Ejb3.0
#Stateless
public class ProcessorBean {
#EJB
private Dao dao;
public void process() {
//logic to be tested
}
}
There's some support in OpenEJB you might find useful in combination with mocking.
As an alternative to the EJB 3.0 Embedded EJBContainer API, you can simply build your app up in code.
import junit.framework.TestCase;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.StatelessBean;
import org.apache.openejb.junit.ApplicationComposer;
import org.apache.openejb.junit.Module;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.ejb.EJB;
#RunWith(ApplicationComposer.class)
public class ProcessorBeanTest extends TestCase {
#EJB
private ProcessorBean processorBean;
#Module
public EjbJar beans() {
EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new StatelessBean(ProcessorBean.class));
ejbJar.addEnterpriseBean(new StatelessBean(MockDao.class));
return ejbJar;
}
#Test
public void test() throws Exception {
// use your processorBean
}
}
Here we see a testcase run by the ApplicationComposer. It is a simple wrapper for a JUnit test runner that looks for #Module methods which can be used to define your app.
This is actually how OpenEJB has done all its internal testing for years and something we decided to open up in the last few releases (since 3.1.3). It's beyond powerful and extremely fast as it cuts out classpath scanning and some of the heavier parts of deployment.
The maven dependencies might look like so:
<dependencies>
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0-3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!--
The <scope>test</scope> guarantees that none of your runtime
code is dependent on any OpenEJB classes.
-->
<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<version>4.0.0-beta-1</version>
<scope>test</scope>
</dependency>
</dependencies>
If you want to mock any object or create its stub, you can use additional library, e.g. Mockito. It allows you very easily mock any class or even an interface.
Additional reading:
Mocks Aren't Stubs by Martin Fowler
Series of Effective Mockito articles on DZone
Related
I want to verify Json.parse is never called in a function using unit test but i'm not using mockito correctly. would appreciate some help.
tried -
when(Json.parse(wsResponse.getBody())).thenThrow(new Exception("error msg"))
but got an error -
java.lang.RuntimeException: java.lang.NullPointerException although method signature includes throws Exception.
tried to use verify and never but verify waits for a function
something like this -
verify(Json.parse(wsResponse.getBody()),never());
but got an error - org.mockito.exceptions.misusing.NotAMockException: Argument passed to verify() is of type ObjectNode and is not a mock!
src I looked at -
https://www.baeldung.com/mockito-exceptions **
How can I test that a function has not been called? **
How to verify that a specific method was not called using Mockito? **
mockito testing verify with 0 calls
The Json.parse method is a static method. In order to mock it and test you will need powermockito. So please add those dependencies:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
After adding those you can run tests with powermockito by including those lines:
#RunWith(PowerMockRunner.class)
#PrepareForTest(fullyQualifiedNames = "com.test.*")
Then if you want to mock a static method you do it like this:
PowerMockito.mockStatic(Json.class);
Then you need to call the method under test:
underTest.testMethod();
In the end you need to tell powermockito that we want this method to be never called:
PowerMockito.verifyStatic(Mockito.never());
Json.parse(Mockito.any());
#Guts answer is more general but requires PowerMockito (another lib), it is still good in my opinion.
This one also worked for me -
I mock the wsResponse and checked if the returned body is not a type of json or csv. it gives me the same result (in my specific case), although it is not as general.
when(wsResponse.getBody()) .thenReturn("{}") .thenReturn("Version Id,Version Name");
I want to mock a static method in JUnit 5. But unfortunately, JUnit 5 doesn’t support Mockito. Is there another method to achieve the same other than reverting back to JUnit 4?
From Mockito 3.4.0 (2020-07-10), it is possible to mock static methods out of the box even in JUnit 5, without any extension.
In the documentation, you can find an example: 48. Mocking static methods (since 3.4.0)
Important note: You need to use inline mock maker. So the dependency to use is not the core one:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.4.6</version>
<scope>test</scope>
</dependency>
Example:
Class under test:
package teststatics;
public class FooWithStatics {
public static Long noParameters() {
return System.currentTimeMillis();
}
public static String oneParameter(String param1) {
return param1.toUpperCase();
}
}
Test class:
package teststatics;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class FooWithStaticsTest {
#Test
void testStatic() {
// Before mock scope, usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Mock scope
try (MockedStatic mocked = mockStatic(FooWithStatics.class)) {
// Mocking
mocked.when(FooWithStatics::noParameters).thenReturn(0L);
mocked.when(() -> FooWithStatics.oneParameter("xxx")).thenReturn("yyy");
// Mocked behavior
assertEquals(0L, FooWithStatics.noParameters());
assertEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Verifying mocks.
mocked.verify(times(1), FooWithStatics::noParameters);
mocked.verify(times(1), () -> FooWithStatics.oneParameter("xxx"));
}
// After mock scope returns to usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
}
}
The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.
With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:
A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.
The reason why Mockito doesn't provide static methods mocking at the moment is because of the common belief that static method shouldn't need to be mocked.
However, there is an open item for Mockito here that discusses the issue.
While this doesn't answer your question, in general it tells you why you shouldn't need the feature at all or will allow you to join the conversation with your ideas.
Make sure to have mockito-inline dependency in your POM file
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
In my case I had to test scenario where exception thrown static method encode() of URLEncoder Class, so for that
try (MockedStatic theMock = mockStatic(URLEncoder.class)) {
theMock.when(() -> URLEncoder.encode("Test/11", StandardCharsets.UTF_8.toString()))
.thenThrow(UnsupportedEncodingException.class);
when(restClient.retrieveByName("Test%2F11")).thenReturn(null);
Assertions.assertThrows(ResponseStatusException.class, ()->service.retrieveByName("Test/11"));
}
We can mock a static method by JMockit.
JMockit is used for mocking the external dependencies outside the test boundary, similar to Mockito and other such mocking libraries.
The most important feature of JMockit is that it lets us mock anything, even the things that are hard to mock with other libraries such as constructors, static and final methods. It even allows mocking the member fields and initialization blocks as well.
Follow the below steps to enable JMockit:
The JMockit artifact is located in the central Maven repository, add the JMockit dependency in pom.xml
<!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.49</version>
<scope>test</scope>
</dependency>
Mock the Class method in TestClass:
public class TestClass{
#Test
public void testMethod() {
new MockUp<ClassName>(){
#Mock
//mock the method here
};
}
}
Follow the tutorial to know more about how to use the JMockit.
In JUnit 4, I use the following setup to test my EJB beans:
#RunWith(EJBContainerRunner.class)
public class MyEETestWithOneOpenEJB {
#Inject
private ACdiBean bean;
#Test
public void theTest() {
// do test
}
}
But in JUnit 5, there is no #RunWith(...) anymore.
Question: How to test with JUnit 5?
You will need to write your own EJBContainerExtension to replace the Runner or find an already existing one. The latter is unfortunately not very likely at this moment, JUnit5 is still not in GA and there are not many official extensions yet.
If you want to, read about JUnit 5 extension model here
TomEE 8 (since 8.0.7) supports testing with JUnit 5 only (without a transient dependency towards JUnit 4).
The Legacy Way
The legacy EJBContainerRunner was replaced by a related JUnit 5 extension.
If you are using Maven, you would need to add the following dependency to your pom file:
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-junit5-backward</artifactId>
<version>8.0.9</version>
<scope>test</scope>
</dependency>
Subsequently, you can replace
#RunWith(EJBContainerRunner.class)
with
#RunWithEjbContainer
which is a pure JUnit 5 extension. There is no need to add any JUnit 4 dependency into your classpath. A usage example can be found in the module's test source at the TomEE GitHub repository.
The Modern Way
In the same release, the ApplicationComposer was enhanced to support JUnit 5 as an extension. To use it, add
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>openejb-junit5</artifactId>
<version>8.0.9</version>
<scope>test</scope>
</dependency>
to your classpath. ApplicationComposer does not require classpath scanning and is faster than the alternative mentioned above.
Just add #RunWithApplicationComposer to your JUnit 5 test class. By default, the container lifecycle is bound to the lifecycle of the test instance. However, other modes are available as well:
PER_EACH: A container is started for each test method
PER_ALL: A container is started for each test class
PER_JVM: A container is started once per JVM
AUTO (default): A container is started based on the test instance lifecycle.
An example can be found in the examples section of the TomEE GitHub repository.
I need several profiles for deployment. In the Maven POM I have defined a profile "dev" and a property "theHost" (as localhost):
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault> <!-- use dev profile by default -->
</activation>
<build>
</build>
<properties>
<theHost>localhost</theHost>
</properties>
</profile>
...
I have activated filterDeploymentDescriptor on the maven-ejb-plugin in order to tell it to filter (substitute) values in ejb-jar.xml:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>2.3</version>
<configuration>
<ejbVersion>3.1</ejbVersion>
--> <filterDeploymentDescriptor>true</filterDeploymentDescriptor>
</configuration>
</plugin
Finally, in ejb-jar.xml I refer to ${theHost} to obtain the desired profile-specific value for the #Resource attribute "host":
<session>
<ejb-name>MongoDao</ejb-name>
<ejb-class>com.coolcorp.MongoDao</ejb-class>
<session-type>Stateless</session-type>
<env-entry>
<env-entry-name>host</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>${theHost}</env-entry-value>
</env-entry>
...
This all works great with a regular Maven build. But when I run an EJB unit test using the Embedded Enterprise Bean Container of GlassFish [EJBContainer.createEJBContainer()], the maven-ejb-plugin seems to ignore filterDeploymentDescriptor=true. The EJB sees "${theHost}" instead of "localhost" although I run maven with the same "dev" profile.
mvn.bat -Pdev test
Does anybody have an idea why the substitution does not work when running a unit test? Is there something more I have to define especially for the unit test so that filtering of ejb-jar.xml occurs? Or a better approach to unit testing EJBs if different profiles exist?
Ideally, you would be able to specify an external "binding" for the env-entry. I know that's possible to do with WebSphere Application Server (via EnvEntry.Value properties), but I don't know if it's possible with Glassfish.
As a workaround, you could declare the env-entry for injection, and then check in PostConstruct whether any value was injected by the container (i.e., don't specify env-entry-value until you're deploying into the server). If you're using JNDI only, you can do the same thing with try/catch(NameNotFoundException).
#Resource(name="host")
private String host;
#PostConstruct
public void postConstruct() {
if (host == null) {
// Not configured at deployment time.
host = System.getProperty("test.host");
}
}
Workaround based on bkail's suggestion: Set a system property only for unit tests and discover it in postConstruct:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.14.1</version>
<configuration>
<skip>false</skip>
<argLine>-Xmx1g -XX:MaxPermSize=128m</argLine>
<reuseForks>false</reuseForks> <!-- with reuse the EJB timer service would fail -->
<systemPropertyVariables>
<is.unittest>true</is.unittest>
</systemPropertyVariables>
</configuration>
</plugin>
And then in the Java method annotated with #PostConstruct:
// Override values that were not substituted in ejb-jar.xml
if (Boolean.getBoolean("is.unittest")) {
host = "localhost";
port = "27017";
authenticationRequired = false;
}
Background:
According to this issue http://java.net/jira/browse/JERSEY-623, InMemoryTestContainer won't be able to support resource with injectable constructor. So if you have, say, HttpServletRequest injected in your resource class, you will need other test containers, such as grizzly.
My case:
I am using dropwizard which is running Jersey underneath, I have HttpServletRequest injected in my resource class like this:
#Context HttpServletRequest request;
my test class looks like:
import com.yammer.dropwizard.testing.ResourceTest;
public class MyResourceTest extends ResourceTest {
// all the tests go here
}
Question:
How to switch the test container from InMemoryTestContainer to grizzly?
I've not had to do this myself, but this gist might be of some help.
The relevant parts are the use of
new JerseyTest(new GrizzlyWebTestContainerFactory())
and the inclusion of
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<version>1.12</version>
</dependency>
In the pom.xml.