JMockit: How to debug tests when using the #Tested annotation? - java

The questions Debug Partial Mock in JMockit and Debugging Java code tested with Spock and JMockit already deal with the issue, that breakpoints in your software under test (SUT) get ignored when the class gets redefined/instrumented by JMockit.
The recommended solution is that you should add an additional breakpoint in your testclass in order to re-activate the breakpoints in SUT, once the execution stops in your testclass.
However, this solution does not work, if you are using the #Tested annotation in your testclass, because in this case the breakpoint in the testclass itself gets ignored.
Here is an example:
package de.playground;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import mockit.Expectations;
import mockit.Injectable;
import mockit.integration.junit4.JMockit;
#RunWith(JMockit.class)
public class DebuggingWithJMockitTest {
public interface Collaborator {
String execute(String... args);
}
public static class ToTest {
private Collaborator collaborator;
public ToTest(Collaborator collaborator) {
this.collaborator = collaborator;
}
public String doSomething() {
return collaborator.execute("a", "b");
}
}
#Injectable
private Collaborator collaborator;
#Tested
private ToTest toTest;
#Test
public void testHoldOnBreakpoint() {
new Expectations() {{
collaborator.execute((String[]) any); result = "whatever";
}};
String result = toTest.doSomething(); // add breakpoint here
assertThat(result, is("whatever"));
}
}
In this case the debugger does not stop in the String result = toTest.doSomething(); line. If you do not use the #Tested annotation and initialize the SUT in a #Beforemethod like this:
// #Tested is not used
private ToTest toTest;
#Before
public void before() {
toTest = new ToTest(collaborator);
}
the breakpoint works perfectly fine.
Is there any workaround how you can debug your code even if you are using the #Testedannotation in the test class?

This bug was brought up on the JMockit Google Group:
Yes, the problem is known, and already solved in JMockit 1.24.
It doesn't look like there was an issue logged for it. Our team ran into this issue on JMockit 1.23 and indeed was able to overcome it by upgrading to JMockit 1.24.

Related

JUnit setUp doesn't create object

Why do I get a NullPointerExeption for testManuscript when trying to run my test?
This is my Manuscript.java:
package org.lhoffjann;
public class Manuscript {
private String msID;
private String path;
public void setMSid(String msID){
this.msID = msID;
}
public String getMSid() {
return this.msID;
}
}
This is my ManuscriptTest.java:
package org.lhoffjann;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ManuscriptTest {
private static Manuscript testManuscript;
#Before
public void setUp(){
testManuscript = new Manuscript();
}
#Test
public void testGetMSid() {
testManuscript.setMSid("1234");
assertTrue("1234" == testManuscript.getMSid());
}
}
You have imported #Test from JUnit 5, while you're using #Before from JUnit 4, that combination doesn't work. You need to choose which JUnit version you want to use, 4 or 5, and then consistently import classes from that JUnit version. I would recommend using JUnit 5, and removing all JUnit 4 dependencies from your classpath, or at least configure your IDE to not suggest those imports.
For this specific case, replace #Before (org.junit.Before) with #BeforeEach (org.junit.jupiter.api.BeforeEach).
In the example as shown, you don't even need this setUp method, as each test-execution gets its own instance of the test class. You can use:
private Manuscript testManuscript = new Manuscript();
That is, remove static, initialize the field directly, and remove the setUp method.
Even if you continue to use the setUp method, I recommend removing the static, so testManuscript is an instance field, like it is actually used.
You have mixed Junit4 with Junit5. You should use only one version.
Junit4 or
package org.lhoffjann;
import org.junit.Before;
import org.junit.Test;
import org.junit.Assert;
public class ManuscriptTest {
private static Manuscript testManuscript;
#Before
public void setUp(){
testManuscript = new Manuscript();
}
#Test
public void testGetMSid() {
testManuscript.setMSid("1234");
Assert.assertEquals("1234",testManuscript.getMSid());
}
or
Junit5
package org.lhoffjann;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ManuscriptTest {
private static Manuscript testManuscript;
#BeforeEach
public void setUp() {
testManuscript = new Manuscript();
}
#Test
void testGetMSid() {
testManuscript.setMSid("1234");
Assertions.assertEquals("1234", testManuscript.getMSid());
}
}

NPE while running test in Spring application (JUnit 5, Mockito 3, Spring JPA repositories)

I'm running a basic Spring App with Mockito 3.1.0 and and Junit 5.5.2. I have a Service call that includes two Spring Data JPA repositories. These are passed into the constructor for DI (along with two others that are immaterial – I'm including them in case they could also, potentially, cause errors.) I see no issues with the service when the app runs.
When I run my test, I get a NPE for myService. Stepping through the stack trace, hasn't really shown me anything that relates to the error. I have also tried (following this Article: https://www.baeldung.com/mockito-junit-5-extension) updating my test class to look like this:
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class) // This dependency doesn't seem to exist
public class MyServiceTest {
// ...
#BeforeEach
// not the JUnit4 #Before annotation.
// Interestingly, this gives me NPEs for the repositories, not the service.
public void setup(){
// ...
}
}
to no avail. What I suspect is happening is that something about my setup isn't properly wired up – either as dependencies or syntax for DI.
How do I debug this? What am I missing? Thanks in advance!
Service:
import org.springframework.stereotype.Service;
#Service
public class MyService {
private final Repository1 repository1;
private final Repository2 repository2;
private final Repository3 repository3;
private final Repository4 repository4;
public MyService(Repository1 repository1,
Repository2 repository2,
Repository3 repository3,
Repository4 repository4) {
this.repository1 = repository1;
this.repository2 = repository2;
this.repository3 = repository3;
this.repository4 = repository4;
}
public Boolean computeValue(String someInput) {
// does computations with repository1, repository2.
}
}
Test:
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock
private Repository1 repository1;
#Mock
private Repository2 repository2;
#Mock
private Repository3 repository3;
#Mock
private Repository4 repository4;
#InjectMocks
private MyService myService;
#Before
public void setup {
when(repository1.findAll()).thenReturn(new ArrayList<>());
when(repository1.findAllByInput(anyString())).thenReturn(new ArrayList<>());
// Yes; I'm aware that this could also be a call to
// MockitoAnnotations.initMocks(this). I've tried it:
// it doesn't work. Also, I've intentionally not taken this
// approach due to reasons:
// - https://stackoverflow.com/questions/10806345/runwithmockitojunitrunner-class-vs-mockitoannotations-initmocksthis
}
#Test
void callMyService() {
assertTrue(myService.computeValue("123"));
}
}
Sample Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// This is just an example, but this pattern is repeated against all
// repositories in the project.
#Repository
public interface Repository1 extends JpaRepository<Repository1, String> {
}
Edit:
I forgot to mention that I have other files in this project that are using exactly these annotations (#RunWith(MockitoJUnitRunner.class), #Mock, #InjectMocks, #Before) that are not failing.
I updated the files with the relevant imports, and added an example of RepositoryN.
I update the MyService class to better reflect the parameters.
For anybody else who encounters this in the future, we were able to fix this problem by changing one of the imports from:
import org.junit.jupiter.api.Test;
to
import org.junit.Test;
Edit:
This had to do with differing versions of JUnit. There's a good long-form explanation as to why here.
Seems like your object myService, is not instantiated. I would suggest not use #InjectMocks and directly create your object as your repositories are already instantiated.
MyService myService = new MyService(..., ..., ...)
I suppose you have to annotate your test class with #ExtendWith(SpringExtension.class) and not with MockitoExtension.class
More info here Junit 5 with Spring Boot: When to use #ExtendWith Spring or Mockito?

Getting NotAMockException on the below Test Case

I have tried to run the below test and am facing NotAMock Exception and not sure how to resolve it. I have been trying to read the concept that methods of class under test cannot be mocked but I am unable to come clear on the subject. If someone could explain me the Why on my own example, I am hopeful of understanding it better.
I tried various ways of changing #RunWith runners for Unit or Integration test setup or using #Spy instead of #Mock or not have #Autowired etc but either was facing dao Null Pointer or Not a Mock Exception variably.
Am I supposed to Use another class and inject the Listener in that class and mock the listener to achieve the functionality of being able to mock the methods and capture the arguments dynamically. Will this work because it is no more the class under test and therefore the methods could be mocked? If so, how is this realized. If not, what is the right way. My sense is moving the listener to another class will only extend my current set of issues of not being able to mock but does not resolve it. However, I am not sure what is the right outcome.
#Component
public class FileEventListener implements ApplicationListener<FileEvent> {
#Autowired private FetchFileDetailsDAO fileDao;//Dao is annotated with #Transactional
#Override
public void onApplicationEvent(FileEvent event) {
fileDao.getDetailsForFile(event.fileName())
}
}
-----------------------------------------------------------------------------------------
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
public class TestClass {
#Captor private ArgumentCaptor<Object> captor;
#Mock #Autowired private FetchFileDetailsDAO dao;
#InjectMocks #Autowired private FileEventListener listener;
#Before
public void setup() throws IOException {
MockitoAnnotations.initMocks(this);
}
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
You are mixing things up here. There is an important difference between #Mock and #MockBean.
You use the first annotation if you want to write a unit test without any Spring Context support (speak #SpringBootTest, #DataJpaTest, etc.). For such tests, you can use #Mock and #InjectMocks.
As you are writing an integration test (you are starting the whole context with #SpringBootTest), you work with managed Spring beans inside your test. Hence you are not writing a unit test anymore.
If you want to replace a Spring bean with a mocked version of it inside your Spring Test Context, you have to use #MockBean:
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
#RunWith(MockitoJUnitRunner.class) // will do the Captor initialization for you
public class TestClass {
#Captor
private ArgumentCaptor<Object> captor;
#MockBean
private FetchFileDetailsDAO dao;
#Autowired
private FileEventListener listener;
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
// ...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
Starting the whole context however for this test is IMHO overkill. You are better off writing a good old unit test with just JUnit and Mockito.
In addition to this, also I would rethink what benefit your current tests adds to your project as it is literally duplicating the business logic. Maybe there is more code that is not present here.
You can find a more detailed summary for the difference between #Mock and #MockBean in this article.
I think the problem is the following line
#Mock #Autowired private FetchFileDetailsDAOImpl dao;
Try #Mock private FetchFileDetailsDAOImpl dao; instead

How to get away with NPE for #Inject field while unit testing?

Im writing unit tests for the below code, Im getting NPE even though Im mocking the fields, How can I resolve that. Those fields are present with #Inject annotation
#Component
interface A {
void run();
}
class B {
#Inject
A a;
void someMethod() {
a.run();
}}
class C{
#Inject
B b;
void anotherMethod() {
b.someMethod();
}
}
class CTest {
#Mock
B b;
// remains null when invoked in the actual class though its mocked instance is
// present here
#Mock
A a;
//// remains null when invoked in the actual class though its mocked instance
//// is present here
#InjectMocks
C c;
#Before
public void initialize() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
c.anotherMethod();
}
}
So how can I get the mocked value in the actual class where the field is injected with #Inject using Mockito?
My guess is that you should annotate your CTest class with #RunWith(MockitoJUnitRunner.class) and remove your before-method as it will become unecessary (#RunWith will do the trick of injecting mocks).
UPDATE
Actually I ran your code in my IDE. And everything is just fine, no NPE. Probably you have to check that your imports are correct. Here are mine to compare with yours:
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.inject.Inject;
import org.springframework.stereotype.Component;
Also, please, pay attention that you declared Class C with upper case letter (should be class C) in your question, thus this very code won't compile.

JUnit 4.x : why is #Before never executed?

When I create an and run unit-test, (in Eclipse (Galileo) with JUnit 4.5 or 4.82),
the #Before is never executed (?).
Below is some sample-code. I would expect the output to be :
initialize
testGetFour
But it is just :
testGetFour
#BeforeClass and #AfterClass are never executed either.
Can someone tell me how come ?
public class SomeClass
{
public static int getFour()
{
return 4;
}
}
//---
import org.junit.Before;
import org.junit.Test;
import junit.framework.TestCase;
public class TestSomeClass extends TestCase
{
#Before
public void initialize() // This method will never execute (?!).
{
System.err.println("initialize");
}
#Test
public void testGetFour()
{
System.err.println("testGetFour");
assertEquals(4, SomeClass.getFour());
}
}
Because you're extending TestCase. This is a JUnit3 class, and so Eclipse treats it as such. JUnit 4 does not require the test class to extend anything.
Remove that, it should work fine.
You should not extend from TestCase (JUnit 3 way of using JUnit) and it will work.

Categories

Resources