Mocking local object created by spring application context using Mockito - java

I am trying to mock a local object, using Mockito Framework, that is being created from the spring application context; But every time I run the application its fails to replace the original object with the mocked object.
Here is the original class's code spinets:
public void executeMyJob(){
ApplicationContext springContext = ApplicationContextUtil.getApplicationContext();
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
List<Brand> dataList =music.getAll();
......
}
I want to mock the MusicEAO so when the getAll() method is called, it will use the mock object.
Below is my test class code snippets:
#Mock
MusicEAO musicEAO;
when(musicEAO.findAll()).thenReturn(myDefinedList);
How can I resolve this issue?

It's hard to tell from the cod you posted but the problem might be that you are mocking MusicEAO in your test but the code you are executing is using a Spring ApplicationContext to get a reference to the MusicEAO bean.
Your original class should not use MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO"); but instead have the bean injected by Spring using #Autowired through a constructor or a setter method (or other dependency injection method). You test will then be able to easily inject a mock version.
class MyJobClass {
MusicEAO music;
#Autowired
public MyJobClass(MusicEAO musicEao) {
this.music = musicEao;
}
public void executeMyJob(){
List<Brand> dataList =music.getAll();
......
}
}
When you say
every time I run the application it fails to replace the original
object with the mocked object
You shouldn't have to run the application to run a unit test for this class - are you asking how to inject mocks into a running application?
.

It doesn't work this way. In your current code :
The following instance is the one in your test :
#Mock MusicEAO musicEAO;
But in your production code, you are using Spring to acquire the Music instance :
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
Nowhere you seem to say to spring that you want the Music mock to be affected to MusicEAO bean name.
If you are doing a Unit Test I would recommand you to avoid messing with Spring, if that's an integration test, then you'll have to find a way to create the mock and pass it over to spring.
For example you can create the mock in the Spring context, autowire it in your test.
Also, I wouldn't recommand the use of static calls in this situation to acquire the Spring context in order to finaly get a hold on the Music object. it makes me think of Spring as a registry which isn't really the case. Spring is a container. Instead you should try to refactor your code in such a way that the Music bean is wired (#Autowired, setters, etc.) in ExecutionJob.
Then it would be even easier to write a Unit Test with JUnit and Mockito, with annotations like #Mock and #InjectMocks.
Hope that helps.

Related

How to inject mock for only one test case with Quarkus/RestAssured

I'm attempting to test a REST controller (using Quarkus) endpoint using rest assured. I want to mock a class that is injected into that controller (ideally with Mockio), but only for one of my tests. Or get different behaviour per test case without having to have separate classes for each test. I'm not sure how to do this?
I've seen doing it the way from the documentation:
#Mock
#ApplicationScoped
public class MockExternalService extends ExternalService {
#Override
public String service() {
return "mock";
}
}
But this would only allow me to use one mock for all tests and not mock certain behaviours based on tests as I would with Mockito. I think?
I've tried creating a mock and annotating it with #Mock
#Mock
public TableExtractorService tableExtractorServiceMock = Mockito.mock(TableExtractorService.class);;
but I still get my real implementation when I use it. I'm using a constructor annotated with #Inject in my Controller that takes the TableExtractorService.
For a bit more information my test using restassured looks like this:
InputPart filePart = Mockito.mock(InputPart.class);
Mockito.when(tableExtractorServiceMock.Extract(anyObject()))
.thenThrow(IOException.class);
final InputStream inputStream = filePart.getBody(InputStream.class, null);
given()
.multiPart("file", inputStream)
.when().post("/document")
.then()
.statusCode(500);
That endpoint calls the service class that I'm trying to mock, and I want that mock to return an exception.
It can't be done. Quarkus documentation explains the issue:-
Although this mechanism is fairly straightforward to use, it nonetheless suffers from a few problems:
A new class (or a new CDI producer method) needs to be used for each bean type that requires a mock. In a large application where a lot of mocks are needed, the amount of boilerplate code increases unacceptably.
There is no way for a mock to be used for certain tests only. This is due to the fact that beans that are annotated with #Mock are normal CDI beans (and are therefore used throughout the application). Depending on what needs to be tested, this can be very problematic.
There is a no out of the box integration with Mockito, which is the de-facto standard for mocking in Java applications. Users can certainly use Mockito (most commonly by using a CDI producer method), but there is boilerplate code involved.
Link for reference: https://quarkus.io/blog/mocking/
According Quarkus test documentation, you can do it usingo #QuarkusMock or #InjectMock.
As #Ankush said, a class annotated with the #Mock annotation is using the CDI #Alternative mechanism, and will be global. #QuarkusTestProfiles can be used to define CDI #Alternatives for groups of tests.
For example, instead of annotating the mock with #Mock, it could be referenced in a test profile as
default Set<Class<?>> getEnabledAlternatives() {
return Set.of(MyMockThing.class);
}
Any test annotatated with the
#TestProfile(MyMockyTestProfile.class)
profile would get those mocks, while others would use the original implementation.
What may be a simpler method is to just use #InjectMock. For example, in the test class, declaring a field like this:
#InjectMock
MyThing mock;
will ensure that mock is used by the classes under test, just for this test.
For rest clients, it will also be necessary to add a #RestClient annotation, and if the original implementation is a singleton, convertscopes can be used to coax the scopes into something mockable.
#RestClient
#InjectMock(convertScopes = true)
MyThing mock;
Behaviour can be added to the injected mock in #BeforeEach or #BeforeAll methods. For example
#BeforeEach
public void setup() {
when(mock.someMethod()).thenReturn("some value");
}

Junit method invocation fails due to spring injection

I have written Junit test class to test particular method. One of the variables being processed in this method is spring injected, by taking the value from properties file.
Below is my test method
#Test
public void myTestMethod() {
//invoking the method to be tested
Assert.assertTrue(updateGroceries());
}
This is the class to be tested,
public class ToBeTested {
//Spring injected value
String categories;
public boolean updateGroceries() {
List<String> categoryList = StringUtils.convertStringToList(categories);
}
In the above class, categories variable is spring injected.
This is properties file content:
categories = Dals,Pulses,Dry Fruits,Edible Oil
Now while running my Junit method, execution is failing because dependency injection is failing.Since the code I want to test runs on tomcat. I want to test the code without running tomcat. Please suggest some solution.
First of all to run mockito you need to enable it over your test.
Using annotation #RunWith(MockitoJunitRunner.class) or execute at the beginning of your test Mockito.initMocks().
Then your test should look like:
#RunWith(MockitoJunitRunner.class)
private YourTest{
#InjectMocks
ToBeTested toBeTested;
#Mock
ToBeTestedDependency dependency;
#Before
public void setUp(){
ReflectionTestUtils.setField(toBeTested, "categories",
"someCategory");
}
#Test
public void shouldDoThisOrThat(){
toBeTested.updateCategories();
}
}
Unfortunately mockito doesn't support injecting #Valueannotated field. You need to use ReflectionTestUtils or setup run your test with SpringJUnit4ClassRunner where you need to define your spring context with PropertyPlaceholder configuration to resolve property that you have as your Value key. There you can find reference to documentation and example of spring testing approach.
Hope this helped.
You should look at Mockito. When you use mockito framework, you can create mocks for spring injected values. You should read more on mockito website.

Mocking aspects, testng and spring 4

I have a very basic scenario where I just need to call a method which has an annotation. This annotation simply calls an AspectJ advice. I just need to make sure that the advice is being called, ideally via a mock verify. Tests are being run using TestNG and mocking using Mockito. Spring is version 4.
class under test
public class MyClassUT
{
#MyAnnotation
public myMethod...
{
...
}
}
test class
#ContextConfiguration(classes = {SpringTestConfig.class})
#WebAppConfiguration
public class MyClassUtTest extends AbstractTestNGSpringContextTests
{
#InjectMocks private MyClassUT mine;
#BeforeMethod
public void init()
{
MockitoAnnotations.initMocks(this);
}
#Test
public void testMyMethod()
{
mine...
}
}
The problem is that the advice is being called and everything is OK, except for the fact that the advice class is instantiated once by spring and another time before calling the said method. The instance being used is the latter which of course has no dependencies injected so it fails. What I am trying to do is provide spring with a mock of my advice or at least inject a mock of the service it depends on and ask AspectJ to use that existing instance.
I have tried using factory methods for the advice, spring test configurations etc, however nothing seems to be working. I have tried also with EnableAspectJautoproxy to no avail, instantiated the aspect with a #Bean annotation, also as a factory method - but nothing works well unfortunately.
(It is also interesting to note that when I enable AspectJ in eclipse, the aspect test also run in maven and as far as I know, nothing changes in pom.xml.)
So, my question is:
How do I make the test use an instance of the aspect I or spring create so that when the method MyMethod is called, its dependencies are in place , or the mock version is used?
This problem is basically equivalent,
but
How do I do this without a single line of XML config - I've seen using an apectOf factory method config being mentioned a lot, but I need a pure annotation solution, if possible;
Works with TestNg not JUnit;
Thank you!

Inject mocks into spring context WITHOUT spring trying to autowire the mock's fields

I have a class which I want to mock for a unit test. The class has a field with an #Autowired annotation. I need to inject that mock into a spring context that is being used in a junit. However, even though it's a mock, spring still sees the #Autowired and goes looking for an instance of this other class to inject. However, the context doesn't have an instance of that class, because one isn't needed (because the methods that might use that field are being mocked).
I've tried both XML and Java config, and in both cases the mock gets created and then spring still tries to find things to autowire into the mock (note I've set autowire=no in both cases).
XML that I've tried:
<bean class="org.easymock.EasyMock" factory-method="mock" autowire="no">
<constructor-arg value="com.stackoverflow.BeanClass" />
</bean>
Java config that I've tried:
#Bean(autowire = Autowire.NO)
public BeanClass beanClass1()
{
return EasyMock.createMock(BeanClass.class);
}
Note I've tried with both EasyMock and Mockito with the same results.
The only way I've found to do this is to do something like context.getBeanFactory().registerSingleton("name", mock) - but I can't do this if using SpringJUnit4ClassRunner.
Before anyone says "just don't do that", this is for an integration test rather than a pure unit test, and the complex wiring logic of some of the beans means that instantiating a spring context here saves a lot of duplicated effort in the test harness.
So, how can I stop spring trying to autowire beans into the fields of my mock?

how to unit test method dependent on springBoot applicationContext?

I am trying to write a unit test for a static method which takes a class and method name and does some reflection to call the method with arguments and store the results. I'm using spring-boot.
My test actually works when I run the full suite, but when I run the test as standalone it fails. The problem is that I've created a mock class (a hand written mock, not using mockito or easymock) which I want the static method to use. However, the reflection can not detect my mock class because the class has not been loaded into the applicationContext by spring-boot. Here is the line that fails:
T proxy = SpringApplicationContext.getBean(clazz);
SpringApplicationContext definition:
#Component
public class SpringApplicationContext implements ApplicationContextAware
{
private static ApplicationContext applicationContext_;
#Override
public void setApplicationContext(ApplicationContext applicaitonContext) throws BeansException {
applicationContext_=applicaitonContext;
}
public static <T> T getBean(Class<T> requiredType) throws beanException {
return applicationContext_.getBean(requiredType);
}
*note, I had to retype by hand, please assume obvious syntax errors are typos.
so basically my applicationContext is not being set or defined. I only need one mock bean in the applicationContext, I could do it by hand, but is there a more spring approach using annotations?
It turns out that my test didn't work rather the were run stand alone or part of a suite, I had a separate issue with using the wrong annotations for #BeforeTest which masked the defect when running the whole suite.
The fix was pretty simple. I added the SpringApplicationConfiguration annotation above my test:
#SpringApplicationConfiguration(classes =
{
MockController.class,
SpringApplicationContext.class
}
public class MyTest extends AstractTestNGSpringContextTests
There are two parts to this. The #SpringApplicationCOnfiguration loads only those values I listed. I could have pointed to configuration classes, but that would ultimately load most of the beans in my enviroment which is overkill for a unit test. So I load the two #component objects needed in my ApplicationContext for my unit test to work only.
I also had to extend AbstractTestNGSpringContextTests because It's the only way to get spring to play nice with the TestNG kit were using for our tests. If others are using junit tests instead of TestNG don't extend the AbstracTestNGSpringContextTests, instead I believe your want to add the annotation:
#RunWith(SpringJUnit4ClassRunner.class)
Though I haven't used it since I'm not using junit.
Hopefully this answer saves others who are trying to figure out how to load only a few classes instead of the entire enviroment (most examples I found want you to load configuration files that will load every bean, which is slow and honestly undesirable in a unit test).
Arguably I should still have mocked out the SpringApplicationContext entirely, I'm lazy and sloppy :)

Categories

Resources