Can we mock the instance if it declared with new key word that is inside the method we are testing? - spring-boot

I'm new to Mockito and Junit, I'm working with Spring Boot
I want to know that can we mock the instance if it declared with a new keyword that is inside the method we are testing?
For example:
#Service
class A {
#Autowired
X x;
#Autowired
Y y;
public void testMe(){
imCommunicatingWithSomeRestClient();
}
private void imCommunicatingWithSomeRestClient(){
String body="";
MyRestClient client=new MyRestClient(iTakeUrlNeedsToHit); //no arg constructor not exist and suppose this is the method of some Core jar project
client.callDataRest(HTTP.GET,body)
}
}
Although I wanted to mock it, I've tried all #Spy #Mock, #InjectMocks to check if it'll behave differently but none of these worked for me as it always creates a new object and calls the real method.
So I change approach slightly and did it with using BeanFactory and instead of new I replace that with :
MyRestClient client=beanFactory.getBean(MyRestClient.class,jobDataRestUrl);
so I have these questions:
Already asked above (if we mock the instance if it declared with new keyword that is inside the method we are testing).
If my current project is Spring Boot project and MyRestClient is inside the jar written in the core. Is the standard say I should not create it by Bean Factory because I think I should do it by that way and let the Spring handles that
I even tried with reflection but it seems it is also not working with the instance created with new keyword inside a method and not on the class level.

Your current setting is not efficiently testable. You may still do it with lots of weird workarounds, but still, not recommended. Here's what you can do; firstly, you should not have any kind of dependency initialization inside your classes (like new MyRestClient(...)). So, move the REST client to the property level and have it injected through constructor.
#Service
class A {
private final X x;
private final Y y;
private final MyRestClient restClient;
public A (X x, Y y, MyRestClient restClient) {
this.x = x;
this.y = y;
this.restClient = restClient;
}
public void testMe() {
imCommunicatingWithSomeRestClient();
}
private void imCommunicatingWithSomeRestClient() {
String body = "";
restClient.callDataRest(GET, body);
}
}
Since you are using Spring, you can create a bean of the REST client and move the endpoint URL to an external property.
class Config {
#Bean
public MyRestClient myRestClient(#Value("${property.name}") String url) {
return new MyRestClient(url);
}
}
Finally, you can easily mock the behavior of that REST client.
#ExtendWith(MockitoExtension.class)
class TestA {
#Mock
private X x;
#Mock
private Y y;
#Mock
private MyRestClient restClient;
#InjectMocks
private A a;
// your tests...
}

Related

Value of doReturn not used

I use spring boot 2.
I search to test a private method in the facade
#RunWith(SpringRunner.class)
#SpringBootTest
public class SamplingsFacadeTest {
#Autowired
private SamplingsFacade facade;
#MockBean
private SamplingsService samplingsService;
#Test
public void exampleTest() throws Exception {
List<Samplings> samplings = new ArrayList<>();
Samplling sampling = new Samplings();
..
samplings.add(sampling);
//fake call
Mockito.doReturn(samplings).when(samplingsService.getSamplingContainingNonCompliantTest());
//real call
List<FactoryEmailNCDto> factoryEmails = Whitebox.invokeMethod(facade, "prepareDataNoncompliantSampling");
}
public List<Samplings> getSamplingContainingNonCompliantTest() {
return samplingsRepository.findSamplingContainingNonCompliantTest();
}
In Facade In
private List<FactoryEmailNCDto> prepareDataNoncompliantSampling() {
List<FactoryEmailNCDto> factoryEmailNC = new ArrayList<>();
List<Samplings> samplings = samplingsService.getSamplingContainingNonCompliantTest();
for (Samplings sampling : samplings) {
...
}
}
Why when I debug, samplings is null and not the value I created
Mockito.doReturn(samplings)
.when(samplingsService.getSamplingContainingNonCompliantTest());
One potential problem is that doReturn takes the form doReturn(value).when(mock).method(), not doReturn(value).when(mock.method()). The latter is considered an incomplete stubbing. Instead, it should look like this:
Mockito.doReturn(samplings)
.when(samplingsService)
.getSamplingContainingNonCompliantTest();
Note that there may be other problems with your test; the code you've written expects samplingsService to be public and non-final, and your getSamplingContainingNonCompliantTest() to likewise be public, non-static, and non-final, but the code sample you have does not confirm that. You may want to call Mockito.validateMockitoUsage() in an #After method, or use a #Rule that handles Mockito annotations and cleanup for you.

code to run before #Autowired is happening

I want to run some lines of code before this line runs:
#Autowired
private SomeClass a;
I'm working on a testing class, I tried #Before, but the #Autowired runs before the code in the #Before, so there is another solution to run code before the autowiring is happening?
This is the code:
Test class:
private classX x;
#Mock
private classA a;
#Autowired
private classB b;
#Before
private void setUpMockResponse() {
when(a.getMeB()).thenReturn(b);
}
classX code:
// Constructor of classX class.
public classX(classA a) {
this.a = a;
this.b = a.getMeB();
}
If there is any solution to the problem I would be happy to see,
the problem is that i need the code in the setUpMockResponse to happen before the autowiring to the classX happens because in the constructor i get b from method on a which is a mock so i need to first set a response when calling the method getMeB(), and also i need b to be autowired.
Sounds like a bad idea to me but who am I to judge? If you want to run code before a dependency is injected, I would write your own setter method and put the #Autowried on the setter, then putting the code before the assignment within the setter:
#Autowired
public void setA(A a)
{
// TODO - put code here
this.a = a;
}
Other option is putting this in an aspect for the setter.
Please note I don't recommend any of these options just trying to answer your question, doing this sort of stuff leads to torturing the next poor soul that comes across this. You won't always need to use Spring in your tests hopefully.

Error with Zipkin in unit test (cannot mock Span.baggageItems)

I am getting this error when I try to run the unitTest in my spring boot application.
java.lang.NullPointerException: null
at org.springframework.cloud.sleuth.Span.baggageItems(Span.java:381)
at org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector.inject(ZipkinHttpSpanInjector.java:27)
at org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector.inject(ZipkinHttpSpanInjector.java:15)
at org.springframework.cloud.sleuth.instrument.web.client.AbstractTraceHttpRequestInterceptor.publishStartEvent(AbstractTraceHttpRequestInterceptor.java:60)
at org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateInterceptor.intercept(TraceRestTemplateInterceptor.java:52)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
I notice that I only get this error when I use this version for spring-cloud-dependencies:
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
but if I use this previous version:
<spring-cloud.version>Camden.SR6</spring-cloud.version>
everything works as I expect
What can I do in order to avoid this error with the last version?
The unit tests are extended this class in order to mock the SpanAccessor
public class AbstractSpanAccessorTest {
#MockBean
private Tracer tracer;
#MockBean
private Span span;
private Random random = new Random();
#Before
public void mockSpan() {
long id = createId();
Span spanMock = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(spanMock.traceIdString()).when(span).traceIdString();
doReturn(span).when(tracer).getCurrentSpan();
doReturn(span).when(tracer).createSpan(anyString());
}
private long createId() {
return random.nextLong();
}
}
Why are you mocking a span? This makes absolutely no sense. Also a Span is never a bean. You already create a normal span via a builder and you should leave that. Assuming that you have set up the Boot context property and you want to mock out tracer bean you should do the following
public class AbstractSpanAccessorTest {
#MockBean
private Tracer tracer;
private Random random = new Random();
#Before
public void mockSpan() {
long id = createId();
Span spanMock = Span.builder().name("mock").traceId(id).spanId(id).build();
doReturn(spanMock).when(tracer).getCurrentSpan();
doReturn(spanMock).when(tracer).createSpan(anyString());
}
private long createId() {
return random.nextLong();
}
}

How to replace a constructor injected object with mocked object in spock

I know that the question is very big but I just want to clear the situation i am into.
I am working on an application that consumes the JMS messages from the message broker.
We are using camel route on the consumer side. All the object required in route builder are injected through constructor injection using spring .
I want to mock the behavior of the actual processing, Once the consumer receives the message from the queue. All the classes gets loaded via the spring configuration.
Below are the three classes:
CustomRouteBuilder.java
public CustomRouteBuilder extends RouteBuilder{
private CustomRouteAdapter customAdapter;
public CustomRouteBuilder (CustomRouteAdapter customAdapter){
this.customAdapter = customAdapter
}
public void configure(RouteDefinition route){
route.bean(customAdapter);
}
}
CustomRouteAdapter.java
public class CustomRouteAdapter {
private Orchestrator orchestrator;
public CustomRouteAdapter (Orchestrator orchestrator){
this.orchestrator = orchestrator;
}
#Handler
public void process(String message){
orchestrator.generate(message) ;
}
}
Orchestrator.java
public class Orchestrator{
private Service service;
public Orchestrator(Service service){
this.service = service;
}
public void generateData(String message){
service.process(message);
}
}
As per our requirement we have to load this configuration file and then write the functional test using spock.
Below is my
CustomRouteBuilderTest.groovy file.
import org.springframework.test.util.ReflectionTestUtils
import spock.lang.Specification
#ContextConfiguration(classes=[CustomRouteBuilderTest.Config.class])
class CustomRouteBuilderTest extends Specification{
private static final String message = "Hello";
Orchestrator orchestrator;
#Autowired
CustomRouteAdapter customRouteAdapter;
def setup(){
orchestrator = Mock(Orchestrator)
ReflectionTestUtils.setField(customRouteAdapter,"orchestrator",orchestrator)
orchestrator.generate(message )
}
private String getMessageAsJson() {
//return json string;
}
private String getMessage() {
// return message;
}
private Map<String, Object> doMakeHeaders() {
//Create message headers
}
private void doSendMessage(){
Thread.sleep(5000)
Map<String,Object> messageHeader = doMakeHeaders()
byte [] message = getMessageAsJson().getBytes()
CamelContext context = new DefaultCamelContext()
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(jmsBrokerUrl)
context.addComponent("activeMQComponent",JmsComponent.jmsComponent(connectionFactory))
ProducerTemplate template = context.createProducerTemplate()
context.start();
template.sendBodyAndHeaders("queueName", message, messageHeader)
}
def "test message consumption"(){
given:
doSendMessage()
}
#Configuration
#Import([FunctionalTestCommonConfig.class,CustomRouteBuilderConfig.class])
#PropertySource(value="classpath:test.properties")
static class Config{
}
}
The problem that here is even though I inject the mocked object to the adapter using ReflectionTestUtils , I am not able to define its behavior correctly.
And when the message is received the orchestrator tries to process it.
My Requirement is that:
Adapter should be called from the camel route automatically which happens but
when the orechestrator.generate is called from the adapter then nothing should happen it should simply return.
But here nothing like that is going on.
Each time I send a message the consumer(RouteBuilder) receives it and calls the handler function which then calls the
orchestrator.generate(message)
function and the orchestrator starts processing and throws an exception from service level.
Any one can please help me on this.
I suppose your beans have been proxified by Spring, and this proxy use cglib (because you see CustomRouteBuilder$$EnhancerBySpringCGLIB$$ad2783ae).
If it's really the case, you didn't #Autowired in your test the real instance of your CustomRouteAdapter but a cglib proxy: Spring creates a new class, extending the realclass, and overriding all the methods of this class. The new method delegate to the real instance.
When you change the orchestrator field, you are in reality changing the orchestrator field of the proxy, which is not used by the real instance.
There are severals ways to achieve what you want to do:
add a setOrchestrator method in CustomRouteAdapter
create the mock in your spring configuration and let spring inject this mock instead of a real instance of Orchestrator
Inject the orchestrator in the real instance (ugly - I didn't recommend you that, it didn't help in the testability of your code!)
customRouteAdapter.targetSource.target.orchestrator = _themock_

How do I mock an autowired #Value field in Spring with Mockito?

I'm using Spring 3.1.4.RELEASE and Mockito 1.9.5. In my Spring class I have:
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Value("#{myProps['default.password']}")
private String defaultrPassword;
// ...
From my JUnit test, which I currently have set up like so:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
I would like to mock a value for my "defaultUrl" field. Note that I don't want to mock values for the other fields — I'd like to keep those as they are, only the "defaultUrl" field. Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
Given this, how can I mock a value for that one field?
You can use the magic of Spring's ReflectionTestUtils.setField in order to avoid making any modifications whatsoever to your code.
The comment from Michał Stochmal provides an example:
use ReflectionTestUtils.setField(bean, "fieldName", "value"); before invoking your bean method during test.
Check out this tutorial for even more information, although you probably won't need it since the method is very easy to use
UPDATE
Since the introduction of Spring 4.2.RC1 it is now possible to set a static field without having to supply an instance of the class. See this part of the documentation and this commit.
It was now the third time I googled myself to this SO post as I always forget how to mock an #Value field. Though the accepted answer is correct, I always need some time to get the "setField" call right, so at least for myself I paste an example snippet here:
Production class:
#Value("#{myProps[‘some.default.url']}")
private String defaultUrl;
Test class:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}",
// but simply the FIELDs name ("defaultUrl")
You can use this magic Spring Test annotation :
#TestPropertySource(properties = { "my.spring.property=20" })
see
org.springframework.test.context.TestPropertySource
For example, this is the test class :
#ContextConfiguration(classes = { MyTestClass.Config.class })
#TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {
public static class Config {
#Bean
MyClass getMyClass() {
return new MyClass ();
}
}
#Resource
private MyClass myClass ;
#Test
public void myTest() {
...
And this is the class with the property :
#Component
public class MyClass {
#Value("${my.spring.property}")
private int mySpringProperty;
...
I'd like to suggest a related solution, which is to pass the #Value-annotated fields as parameters to the constructor, instead of using the ReflectionTestUtils class.
Instead of this:
public class Foo {
#Value("${foo}")
private String foo;
}
and
public class FooTest {
#InjectMocks
private Foo foo;
#Before
public void setUp() {
ReflectionTestUtils.setField(Foo.class, "foo", "foo");
}
#Test
public void testFoo() {
// stuff
}
}
Do this:
public class Foo {
private String foo;
public Foo(#Value("${foo}") String foo) {
this.foo = foo;
}
}
and
public class FooTest {
private Foo foo;
#Before
public void setUp() {
foo = new Foo("foo");
}
#Test
public void testFoo() {
// stuff
}
}
Benefits of this approach: 1) we can instantiate the Foo class without a dependency container (it's just a constructor), and 2) we're not coupling our test to our implementation details (reflection ties us to the field name using a string, which could cause a problem if we change the field name).
You can also mock your property configuration into your test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest
{
#Configuration
public static class MockConfig{
#Bean
public Properties myProps(){
Properties properties = new Properties();
properties.setProperty("default.url", "myUrl");
properties.setProperty("property.value2", "value2");
return properties;
}
}
#Value("#{myProps['default.url']}")
private String defaultUrl;
#Test
public void testValue(){
Assert.assertEquals("myUrl", defaultUrl);
}
}
I used the below code and it worked for me:
#InjectMocks
private ClassABC classABC;
#Before
public void setUp() {
ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
Reference: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/
Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.
One way to resolve this is change your class to use Constructor Injection, that can be used for testing and Spring injection. No more reflection :)
So, you can pass any String using the constructor:
class MySpringClass {
private final String defaultUrl;
private final String defaultrPassword;
public MySpringClass (
#Value("#{myProps['default.url']}") String defaultUrl,
#Value("#{myProps['default.password']}") String defaultrPassword) {
this.defaultUrl = defaultUrl;
this.defaultrPassword= defaultrPassword;
}
}
And in your test, just use it:
MySpringClass MySpringClass = new MySpringClass("anyUrl", "anyPassword");
Whenever possible, I set the field visibility as package-protected so it can be accessed from the test class. I document that using Guava's #VisibleForTesting annotation (in case the next guy wonders why it's not private). This way I don't have to rely on the string name of the field and everything stays type-safe.
I know it goes against standard encapsulation practices we were taught in school. But as soon as there is some agreement in the team to go this way, I found it the most pragmatic solution.
Another way is to use #SpringBootTest annotation properties field.
Here we override example.firstProperty property:
#SpringBootTest(properties = { "example.firstProperty=annotation" })
public class SpringBootPropertySourceResolverIntegrationTest {
#Autowired private PropertySourceResolver propertySourceResolver;
#Test
public void shouldSpringBootTestAnnotation_overridePropertyValues() {
String firstProperty = propertySourceResolver.getFirstProperty();
String secondProperty = propertySourceResolver.getSecondProperty();
Assert.assertEquals("annotation", firstProperty);
Assert.assertEquals("defaultSecond", secondProperty);
}
}
As you can see It overrides only one property. Properties not mentioned in #SpringBootTest stay untouched. Therefore, this is a great solution when we need to override only specific properties for the test.
For single property you can write it without braces:
#SpringBootTest(properties = "example.firstProperty=annotation")
Answer from: https://www.baeldung.com/spring-tests-override-properties#springBootTest
I also encourage you to whenever possible pass property as a parameter in constructor like in Dherik answer (https://stackoverflow.com/a/52955459/1673775) as it enables you to mock properties easily in unit tests.
However in integration tests you often don't create objects manually, but:
you use #Autowired
you want to modify property used in a class that is used in your integration test indirectly as it is deep dependency of some directly used class.
then this solution with #SpringBootTest might be helpful.

Resources