Granny's Programming Pearls
"inside of every large program is a small program struggling to get out"
JavaRanch.com/granny.jsp
Win a copy of Python Continuous Integration and Delivery this week in the Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Bear Bibeault
  • Paul Clapham
  • Jeanne Boyarsky
Sheriffs:
  • Devaka Cooray
  • Junilu Lacar
  • Tim Cooke
Saloon Keepers:
  • Tim Moores
  • Ron McLeod
  • Tim Holloway
  • Claude Moore
  • Stephan van Hulst
Bartenders:
  • Winston Gutkowski
  • Carey Brown
  • Frits Walraven

Difference with unit testing controller and service method  RSS feed

 
Ranch Hand
Posts: 72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey. Im coming to you with question.
I wanted to test same method from controller and service layer.
Here is it:





Here is the test for Controller:


And here is for service;


The question is... Why do I have to use @MockBean annotation in controller, why not @Mock for BookFindOperationsService bookService. Same question for service, why do I need to @Mock repository, why not to use @MockBean?
 
author
Posts: 396
8
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

When testing the service, Spring really isn't involved and you are responsible for injecting the mock into the service (in your case, you're letting Mockito do it with @InjectMocks). In short, there is no Spring container and you (or Mockito) is taking on the responsibility for the lifecycle and injection of the components.

When testing the controller, there *is* a Spring application context and so Spring is responsible for the lifecycle and injection of the components. The @MockBean isn't much different than @Mock from a mocking standpoint, but what makes it special in the case of a Spring test is that it ensure that the mock object is registered as a bean in the Spring application context so that Spring can find it and inject it. If you weren't using @MockBean in this test, Spring will try to do the injection, but not find a bean of that type and will fail.


must Janik wrote:Hey. Im coming to you with question.
I wanted to test same method from controller and service layer.
Here is it:





Here is the test for Controller:


And here is for service;


The question is... Why do I have to use @MockBean annotation in controller, why not @Mock for BookFindOperationsService bookService. Same question for service, why do I need to @Mock repository, why not to use @MockBean?

 
must Janik
Ranch Hand
Posts: 72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Could you tell me, why in service Spring Context is not involved and in controller it is? Where it comes from?
 
Craig Walls
author
Posts: 396
8
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Let's start with BookFindOperationsServiceTest, as it's the non-Spring one. In this case, it is annotated with @RunWith(MockitoJUnitRunner.class), which puts Mockito in charge of setting the test up (not Spring) and thus the BookFindOperationsService, which is annotated with @InjectMocks will be injected by Mockito with the BookRepository mock object. Inside of the test method, you train the mock (the line with "when(bookRepository.findByAuthorAllignoreCase..."). But in no way is Spring involved in this particular test. Mockito handles the injection.

In BookFindOperationsControllerTest, I am assuming that it the class is annotated with @RunWith(SpringRunner.class), and possibly also annotated with @SpringBootTest or @ContextConfiguration to guide the SpringRunner in how to load the application context. In this case, Spring is put in charge of creating the application context, loading it with beans, and...in the case of the BookFindOperationsService instance, loading the mocked bean into the application context. You still train the mock inside of the test method, but rather than Mockito injecting it into the controller, it gets put into the Spring application context as a bean and is injected into the controller by Spring itself.

must Janik wrote:Could you tell me, why in service Spring Context is not involved and in controller it is? Where it comes from?

 
must Janik
Ranch Hand
Posts: 72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yeah, but you said about what is going on when we annotate class with @RunWith(MockitoJUnitRunner.class) or @RunWith(SpringRunner.class). I would like to know, why I can not annotate i.e Service with @RunWith(SpringRunner.class) or controller with @RunWith(MockitoJUnitRunner.class).
 
must Janik
Ranch Hand
Posts: 72
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Still waiting Can not find answer for that.
 
Craig Walls
author
Posts: 396
8
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Well, it *is* the weekend and I was busy with family yesterday, but...

There's no reason why you couldn't use Spring (@RunWith(SpringRunner.class)) in the service test and only a small reason why you couldn't use Mockito in the controller test.

First, you should be able to use @RunWith(SpringRunner.class) in the service test, but when you do that, Spring will become responsible for creating and injecting the beans that will be in the context of the test. That's fine, but it will generally do so by loading all of the beans it can find. You can narrow that down to only a slice of your application and then mock what those beans need (using @MockBean), but how you do that depends on the structure of your app. It'd be hard for me to tell you how to do that in a forum like this without knowing more about the structure of your app, but in short: The @SpringBootTest annotation has a "classes" attribute that lets you pick and choose certain configuration classes so that you can refine what you want in the test and what you don't.

You *could* use @RunWith(MockitoJUnitRunner.class) to test controllers, but then you won't get the benefit of a mocked out Spring MVC to test the controller as a controller (by sending requests to it). That is, the MockMvc that you use to test the controller actually sets up a minimal Spring application context *and* mocks out just enough Spring MVC web framework to be able to test the controller without actually setting up a server. The @MockBean in this test ensure that the mocked service ends up in that application context so that it can be injected into the controller. And...the @SpringBootTest/@RunWith(SpringRunner.class) combination ensures that the MockMvc object is created for you. (In truth, you can actually set all of that up manually and not annotate the class with @SpringBootTest/@RunWith(SpringRunner.class), but it's a lot more work. Prior to Spring Boot, that's how MockMvc was commonly used, but I'm super-happy that I don't have to do it that way anymore.)

Hope that helps. Now I'm going to resume my weekend.


must Janik wrote:Still waiting Can not find answer for that.

 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!