How to pass dynamic values to a constructor from another class which is injected as autowired object - spring-boot

How to pass dynamic values to a constructor from another class which is injected as autowired object?
#Service
Class Test {
int a;
String b;
public Test(int a, String b) {
this.a = a;
this.b = b;
}
}
Class Sample {
#Autowired
Test test;
}
How can I pass values to Test class constructor from Sample class?

Spring beans are immutable by idea.
If your Test instance is different for every instance of Sample class, it don't have to be a spring bean, especially if some fileds on the Test instance depend on values from the Sample instance.
Maybe a solution would be to change your Test class scope so you can create multiple instances, but you have to explain more what you mean by dynamic values.

Related

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

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...
}

mock autowired field in#Import class

I am doning an unit test which require some objects which injected by spring so I use:
#RunWith(SpringRunner.class)
#Import({BConfig.class})
public class ATest {
private A a;
#Autowired
private B b;
#Before
public void init() {
a = new A(b);
}
}
However, the BConfig class has an autowired field c which I don' need in this test:
class BConfig {
#Autowired
C c;
#Bean
public getB() {
return new B();
}
// other method code will use field c
}
The autowired c field will get data from redis in #PostConstruct which don't exist in unit test. If I don't omit that, the unit test will report error due to redis data is not exist.
I have a solution to make C to 2 subclasses CProduction and CUnitTest both of them implements interface C, then active profile to use CUnitTest in unit test. However this is some kind of invasive because if don't do the unit test, the interface of C is useless.
Is there a better way to do this?
Consider using:
#RunWith(SpringRunner.class)
#ContextConfiguration({BConfig.class})
class ATest {
#MockBean // will place a mock implementation of C to the context instead of real one that tries to connect with Redis.
C c;
...
}
Note that it will work seamlessly if C is an Interface. Otherwise it will try to create a proxy by inheritance (something that extends from C) and if C has some redis-related code in constructor it might still attempt to call it.
Update 1
Based on OP's comment in an attempt to clarify the answer:
When spring application context starts it basically resolves all the beans and injects what's needed.
First it resolves the bean definitions (metadata) - the real class, scope, etc.
And then it creates beans in the order that will allow injection. For example, it class A has a field of class B (both are beans) then spring must create B first, then create a and inject B into A.
Now, #MockBean is a hook relevant for tests. It tells spring application context used in test that instead of a regular bean definition that spring is able to parse out of #Configuration classes or find the component due to #Component annotation placed on it, it should use the Mock - something that is generated in runtime with frameworks like Mockito.
This mock implementation can later be used to specify the expectations (see Mockito basic tutorial, there are plenty of those on internet), but what's more important for your case, it wont connect to redis because this mock implementation doesn't have any redis related code.
Update 2
The configuration should be rewritten as follows:
#Configuration
public class BConfig {
#Bean
public getB() {
return new B();
}
#Bean
public C c() {
return new C();
}
// other method code will use field c:
// if for example D uses C: then you can:
#Bean
public D d(C c) {
return new D(c);
}
}

How to mock private method in public method in Spring Boot with JUnit

I'd like you ask a few questions and ask you for advice:
I want to test my public method (I use Spring Boot, Mockito, JUnit):
#Service
public class MyClass{
public Long getClientId(List<String> nameSurname) throws AuthorizationException {
Long operatorId;
if(...){
(... something not interesting ...)
User user = getUserByLogin("AnthonyGates2");
operatorId = nonNull(user) ? user.getOperatorId() : null;
} else {
List<User> users = getUserListByLogin("AnthinyGates");
operatorId = isNotEmpty(users) ? return operatorId;
return operatorId;
}
How to test the method getClientId?
Methods getUserByLogin and getUserListByLogin are private in this class (MyClass) but I have to mock the results of these private methods because these methods retrieve data from an external service.
These private methods looks like:
User user = DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);
DelegateImpl.getDelegate().getUserByLogin get data from database and that data have to be mocked like:
when(DelegateImpl.getDelegate().getUserByLogin(any())).thenReturn(user);
How can I test my public class? Should I use PowerMock/PowerMockito? Making these methods public is in my opinion ugly because these methods are called only in MyClass. I can't find a good tutorial in Internet for my case (Spring Boot, Mockito, JUnit).
Thank you very much for all your tips!
Best regards
Matthew
Test the unit only by calling the public methods. I think that your example is a class in the service layer (contains business logic) and the two getUser... methods should be in a different class (I think in the data layer) where they can be public. Inject that class via the constructor as a dependency (in the service object) so you can mock it when testing the service class. The data layer class (with the getUser... methods) can also be tested by it's own unit tests.
If you are not able to unit test a method/class then it most probably means that it just does too much. Try extracting your private methods to a separate class. It does not need to be public - you can e.g. have it package-local in the same package.
Later, in the test, you would have to inject a mock of this class and simulate its behaviour.
The setup of MyClass in its unit test could look similar to this:
AnotherClass anotherClassMock = Mockito.mock(AnotherClass.class);
MyClass myClass = new MyClass(anotherClassMock);
Where AnotherClass would have methods getUserListByLogin and getUserByLogin.
EDIT:
It seems that the logic within in your private methods already call an external class. The problem is that you obtain an instance of an object via a call to a static getDelegate() method in another class.
Here's what you can do:
Create a new field in MyClass which would have the same type as the one returned by getDelegate() method (I don't know what that is, I'll call it Delegate)
Have 2 constructors: a default one which would assign the result of getDelegate method to your new field and another one which would take an instance of Delegate as a parameter and assign it to your field
In tests use the second constructor to create an instance of MyClass and pass a mock of Delegate class
It would look more ore less like this:
class MyClass() {
private Delegate delegate;
MyClass() {
this.delegate = DelegateImpl.getDelegate();
}
MyClass(Delegate delegate) {
this.delegate = delegate;
}
// ... the rest
}

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.

How to wire them together by Spring annotations

I got a web application, and I have to wire it's components (A,B,C) together instead of direct method calls.
So, I dont know how can I wire object instances together when they need additional instances in the constructor.
I think in thats why the we can use (bean ... constructor-args ...) in application context.
But I dont know what if I want to use annotations.
I made a simple example to demostrate the concrete problem
class A
{
protected B b;
...
public func1(){
obj0 = makeObj0();
b = new B(obj0)
}
public Obj0 makeObj0() {...}
}
class B extends C
{
protected Obj0 obj0;
public B(Obj0 obj0){
super(obj0, "foo", new Obj1); //there is no another way to construct B, C
setObj0(obj0);
}
public void setObj0(obj){ obj0=obj;}
}
What is the best way to wiring A,B,(C) together (by annotations) because B, C needs additinal arguments right in their constructor.
Sorry for the lenghty explanation, and thanks for the tips in advance.
Cs
#Autowired
public B(..) {..}
will autowire all arguments. If you need additional arguments - then don't use constructor injection. Use setter/field injection. Provide an empty constructor and have #Autowired on each field that you need.
Hmm.. if you try with #Resource when initiating the B-class.
class A
{
#Resource(name = "b")
protected B b;
...
public func1(){
obj0 = makeObj0();
b = new B(obj0)
}
public Obj0 makeObj0() {...}
}
Then if you need to Autowire in B or C I find hard to tell.

Resources