How can I inject a mocked bean that has certain behaviour defined into a a class that is under test but when it's initiated the constructor calls that mock and execute certain action against it.
So for example I have this class that I would like to test:
public class A {
#Autowired
private B b;
private String result = null;
public A(int c) {
result = b.calculateStuff(c) + "AA";
}
public String getResult() {
return result + "A";
}
}
Now the test class:
public class ATest{
#Mock
private B b;
#InjectMocks
private A a;
#Before
public void setUp() {
doReturn("String result!").when(B).get(anyInt());
MockitoAnnotations.initMocks(this);
}
public void testGetResult() {
assertEquals(a.getResult(),"String result!AAA");
}
}
How can I actually inject a mock into A ? Is there a better way of approaching this?
As per the documentation the constructor of your object under test has to match the mocks in the test for injection to happen. So you need to redesign your constructor for this to work. An int can't be mocked.
As a general note, you should try to keep your Spring beans stateless, meaning that the only allowed class variables are other stateless beans (so no String, int, other literals). For instance:
public class A {
#Autowired
private B b;
public String getResult(int c) {
return b.calculateStuff(c) + "AAA";
}
}
MockitoAnnotations.initMocks(this); should be the first invocation in the #Before-annotated method.
Also, you have your expected and actual arguments the wrong way.
I assume that if you are injecting a bean in your class A then it needs to be singleton. What are you trying to achieve by creating a constructor and passing an argument to it? If you want to execute something as soon as the bean is created then I would suggest you to replace the constructor with a normal method and use #PostConstruct on it. Example:
#PostConstruct
public void init () {
/* Do Something */
}
You need to tweak some things in case you want to utilize the Testing Frameworks like Mockito up to its full potential.
Tell me if I misunderstood your requirements.
Related
I have a service class that extends another service with a constructor. This class has an autowired field and a method that I wanted to unit test using Mockito. However, I am having trouble writing a unit for it.
Let say the service looks somewhat like this:
#Service
public class SomeService extends Service {
#Autowired
private SomeClient someClient;
public SomeService(Product product, #Qualifier(Constants.SOME_BEAN) Details temp) {
super(product, temp);
}
#Override
public State create() {
Request request = new Request();
request.set...
request.set..
Status status = someClient.createTenant(request);
..
.. // construct a State object and return
}
}
Now, I am writing a test for this class and I am trying to unit test the method create() above. I am having trouble mocking someClient there when this method is called by my test.
The test class looks somewhat like:
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
private Detail temp;
private SomeFacade service;
private SomeClient someClient;
private Product product;
#Before
public void setup() {
temp = mock(Details.class);
product = mock(Product.class);
service = spy(new SomeService(product, temp));
someClient = mock(SomeClient.class);
}
#Test
public void test() {
Status status = new Status();
status.setStatus(...);
when(someClient.createTenant(any())).thenReturn(status);
State state = service.create();
// asserts
}
}
What I want to is that when I call service.create in the test above, the call
someClient.createTenant(request); in the method tested should be mocked to return a value and this is something I am not able to get working. Any help?
We can use Mockito's #InjectMocks-annotation to inject mocks into our unit under test. For this, we also need to
remove mock intialization from the setup()-method and
annotate the mocks with #Mock
Remarks:
Instead of field injection, I would recommend constructor injection. This allows, among other things, to keep the structure of the code and create the unit under test within setup() by manual injection
I would also recommend to add a type to the when: when(someClient.createTennant(any(Request.class)))...
As was pointed out by #second, the spy on the unit under test is superfluous.
Also pointed out by #second was the fact that field injection will not work if a constructor is present
I have a Singleton class that I want to test. It uses a #Inject annotation for that class's contructor. Now for testing I want to call a public method for that class in my test class but unable to do so. I have mocked an object that is getting passed to the constructor.
#Inject
private SomeClass(SomeOtherClassObject obj) {
super(obj);
}
I mocked the above private constructor in the following way:
Singleton mockSingleton = PowerMock.createMock(Singleton.class);
PowerMock.expectNew(Singleton.class).andReturn(mockSingleton);
I dont understand how do I call the following method
public SomeClass someMethod(int 1, String 2){
//some logic
return (Object of SomeClass)
}
Any help will be appreciated. Thank You.
If you are using guice as well, you can provide a module in your test that binds SomeOtherClassObject to your mock instance. Then create the SomeClass instance via Guice's injector.
#Test
public void test() {
SomeOtherClassObject other = ...; // what ever you need to create the Mock
Injector injector = Guice.createInjector(new AbstractModule(){
public void configure() {
bind(SomeOtherClassObject.class)toInstance(other);
}
});
SomeClass some = injector.getInstance(SomeClass.class); // guice takes care of the constructor injection
some.someMethod(...);
}
If you dont use guice, have a look at needle4j. Its a test-support lib that automatically injects mocks when injection is required. But it works only with easymock or mockito.
Let's say I have a class (OrchestratingClass) that has a method orchestrate() that calls a number of smaller methods of other classes (classA's do1() method, classB's do2() method). I would like to test the behavior of orchestrate() by mocking the responses of do1() and do2() with various permutations. I'm running my test with something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public OrchestratingClassTest {
#Inject
private OrchestratingClass oc;
#Test
public void test1() {
// I would like to mock classA's do1() method to send back "1"
// I would like to mock classB's do2() method to send back "2"
}
#Test
public void test2() {
// I would like to mock classA's do1() method to send back "100"
// I would like to mock classB's do2() method to send back "200"
}
static class SimpleConfig {
#Bean
public InterfaceA interfaceA() {
return new ClassA();
}
#Bean
public InterfaceB interfaceB() {
return new ClassB();
}
#Bean
public OrchestratingClass orchestratingClass() {
return new OrchestratingClass();
}
}
}
And the orchestratingClass itself is quite basic, but I've added some sample code to aid in visualization:
#Named
public OrchestratingClass {
#Inject
private InterfaceA interfaceA;
#Inject
private InterfaceB interfaceB;
public String orchestrate() {
return interfaceA.do1() + " " + interfaceB.do2();
}
}
Now I'm aware I could tweak my SimpleConfig class to have the mocked out versions of classA and classB, but then I'm locked into 1 particular mock and can't "re-mock" things when I move onto test2(). I'm convinced that playing around with the java config files for 1 single test class would not work if we're trying to inject different "flavors" of beans "per-test". Does anyone have any recommendation on what I can do to make sure I'm really testing this thoroughly without being invasive (ex: adding superfluous "setters" for the specific classes in the orchestratingClass to side-step the bean injection pain)? Essentially, I'm looking to "tamper" the applicationContext on a per-test basis for specific beans of interest (along with the necessary housekeeping that's required) by applying a variety of mocks.
Here's a simple example using Mockito:
public class OrchestratingClassTest {
#Mock
private ClassA mockA;
#Mock
private ClassB mockB;
#InjectMocks
private OrchestratingClass oc;
#Before
public void prepare() {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldConcatenate() {
when(mockA.do1()).thenReturn("1");
when(mockB.do2()).thenReturn("2");
assertEquals("1 2", oc.orchestrate());
}
}
The magic happens in the call to MockitoAnnotations.initMocks(this), which will create mock instances for the fields annotated with #Mock, then create a real instance of Orchestrating class and inject its fields, thanks to #InjectMocks.
Note that, even without this magic, you could make your class easily testable by just adding a constructor taking ClassA and ClassB as arguments to OrchestratingClass, and annotate that constructor with #Autowired instead of annotating the fields. So, in short, use constructor injection rather than field injection.
Let's say you have the following class you would like to test:
public class SomeService {
public String someMethod(SomeEntity someEntity) {
return someEntity.getSomeProperty();
}
}
The SomeEntity looks like this:
public class SomeEntity {
private String someProperty;
public getSomeProperty() {
return this.someProperty;
}
}
The assertion you would like to do can be the following:
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
How can you make this test work?
1) Add a setter for 'someProperty' in the SomeEntity class. I don't think this a good solution because you don't change production code to make your tests work.
2) Use ReflectionUtils to set the value of this field. Test would look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntity someEntity = new SomeEntity();
ReflectionTestUtils.setField(someEntity, "someProperty", "someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
}
3) You create an inner class in your test class that extends the SomeEntity class and adds the setter for this field. However, for this to work you will also need to change the SomeEntity class because the field should become 'protected' instead of 'private'. Test class might look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntityWithSetters someEntity = new SomeEntityTestWithSetters();
someEntity.setSomeProperty("someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
public class SomeEntityWithSetters extends SomeEntity {
public setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
}
}
4) You use Mockito to mock SomeEntity. Seems fine if you only need to mock only one property in the class, but what if you need to mock like 10 properties are so. The test might look like this:
public class TestClass {
private SomeService someService;
#Test
public void testSomeProperty() {
SomeEntity someEntity = mock(SomeEntity.class);
when(someEntity.getSomeProperty()).thenReturn("someValue");
String result = someService.someMethod(someEntity);
assertThat(result).isEqualTo("someValue");
}
}
you can set the value using reflection. It doesn't need any change in production code.
ReflectionTestUtils.setField(YourClass.class, "fieldName", fieldValue);
You can add a setter with default (package private) scope.
With junit testing of SomeService.someMethod()
alternative 1. should not use this as no need to change entity for writing junit.
alternative 2. can be used.
alternative 3. again same a 3, no need to extend for just junit. how about when the class cannot be extended.
alternative 4. yes, a good option. mockito is being used for the same reason.
What is the behavior / contract specific to SomeService that is testable? Based upon your skeletal code, there really isn't any. It will either throw a NPE on bad input, or return a String that may or may not be null, depending on Hibernate magic. Not sure what you can actually test.
I have been through this same dilemma many times before, a quick solution is to make the field you want to mock package protected, or provide a protected setter. Of course both will alter production code.
Alternatively, you can consider dependency injection framework, such as Dagger. Below is an example they give:
#Module
class DripCoffeeModule {
#Provides Heater provideHeater(Executor executor) {
return new CpuHeater(executor);
}
}
This JUnit test overrides DripCoffeeModule's binding for Heater with a mock object from Mockito. The mock gets injected into the CoffeeMaker and also into the test.
public class CoffeeMakerTest {
#Inject CoffeeMaker coffeeMaker;
#Inject Heater heater;
#Before public void setUp() {
ObjectGraph.create(new TestModule()).inject(this);
}
#Module(
includes = DripCoffeeModule.class,
injects = CoffeeMakerTest.class,
overrides = true
)
static class TestModule {
#Provides #Singleton Heater provideHeater() {
return Mockito.mock(Heater.class);
}
}
#Test public void testHeaterIsTurnedOnAndThenOff() {
Mockito.when(heater.isHot()).thenReturn(true);
coffeeMaker.brew();
Mockito.verify(heater, Mockito.times(1)).on();
Mockito.verify(heater, Mockito.times(1)).off();
}
}
If I have two classes like:
Class A {
public String importantValue = "stringvalue";
#Autowire
public B b;
}
#Component
#Scope("prototype");
Class B {
// This should be set automatically
// from IOC Container upon injection.
public String importantValueFromA;
}
Is this even possible? As soon as B class has been injected to A it should automatically set the value in B.
Do you want class A to do some setup on injected class B? That's simple:
#Service
class A {
private String importantValue = "stringvalue";
#Autowire
private B b;
#PostConstruct
public void initB() {
b.importantValueFromA = this.importantValue;
}
}
Obviously you cannot access b.importantValueFromA in A.A constructor because injection didn't yet happen. But #PostConstruct callback is guaranteed to be called after injection.
Another approach is to use setter injection, but it feels kind of hacky:
private B b;
#Autowire
public void setB(B b) {
this.b = b;
b.importantValueFromA = this.importantValue;
}
Two suggestions:
keep your fields private and use setters/methods to access them.
injecting prototype scoped bean to singleton bean might have some unexpected results. Enough to say only one instance of B will be created.
No. B is created before A (since A depends on B) so it will not update the value itself. You have to use a contructor injection:
Class A {
public String importantValue = "stringvalue";
#Autowire
public A(B b) {
b.importantValueFromA = this.importantValue;
}
}
How about doing something like this:
Declare your class B as a scoped proxy, which underlying will expose a proxy to A instead of the real B and will respect the prototype scope.
#Component
#Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)
class B {
Then you can inject in an attribute of A in B this way:
#Value("#{a.importantValue}")
private String importantValueFromA;
Here is a full working example in a gist:
https://gist.github.com/3395329