0

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?

karruma
  • 768
  • 1
  • 12
  • 32

2 Answers2

2

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.

Tobb
  • 11,850
  • 6
  • 52
  • 77
  • There is nothing wrong in placing `MockitoAnnotations.initMocks(this);` statement in the `@Before` annotated method. – user2004685 Jan 25 '16 at 18:50
  • Never said there was.. Just that it should be the first invocation in the before-method. – Tobb Jan 25 '16 at 22:08
  • OK, that's fine - I understand it - but regardless if it is a default constructor with no parameters being passed, how can I mock certain method of the B class so the mocked method is available in available in the constructor? – karruma Jan 25 '16 at 22:34
  • Well, with your current code you can't. Since B is called in the constructor, there is no way the variable `b` could be assigned a value at the first call in the constructor. – Tobb Jan 26 '16 at 08:29
  • @Tobb Why did you delete your answer in my question? It actually solved the problem? http://stackoverflow.com/questions/35056094 – Koray Tugay Jan 28 '16 at 08:24
1

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.

user2004685
  • 9,548
  • 5
  • 37
  • 54
  • Wow - thanks, I didn't know that there is such annotation. That is partially answering my question - the thing is that I would like to mock a method in class B that will be available in the @PostContstruct method. – karruma Jan 25 '16 at 22:32
  • Your `Injected Mocks` will always be available in `@PostConstruct` :) That's how I write most of my classes. Now, if you are happy then please accept the answer and help us close the question. – user2004685 Jan 26 '16 at 05:36