7

I want to test my spring mvc controller.

The controller has a service:

@Autowired
UserService userService

And my user service depends on (autowired) my UserDao and some other services like mongoDb etc.

Now I want the business logic to be tested in my UserService, but ofcourse I want to mock the responses from my UserDao and Mongodb etc.

How do I setup my unit test correctly?

Can I re-use the spring container's xml file that has all my beans etc. or do I create a new one? (I'm assuming I have to get the spring container involved here)

Looking for some guidance on this, any tutorials would be greatly appreciated.

Update

What I find strange is that for my spring controller (that doesn't implement from Controller) I was able to access my private varialbe to manually set my service, i.e:

@Controller
public class UserController {

   @Autowired
   UserService userService;
}

And in my unit test I could do:

UserController controller = new UserController();
controller.userService = ....

But for my UserService, which has UserDao autowired, I can't access the userDao property:

UserService userService = new UserServiceImpl();
userService.userDao = .... // not available

It makes sense since it is private, but how is it working for my controller?

Blankman
  • 259,732
  • 324
  • 769
  • 1,199
  • 1
    Re: update, what packages are the controller, service, and tests in? If the test is in the same package, it can access default-scoped properties. Omitting an access modifier does *not* make a property private, rather [package-private](http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html). – Dave Newton Jan 02 '12 at 15:26
  • I have them in the same package. Ahh I see, I assumed it was private. I was wondering why IntelliJ during a refactor makes fields private by default. So I should keep them as package private then? – Blankman Jan 02 '12 at 15:31
  • 1
    There's no "should", it just depends. My only concern with package-private is that you could still access something unintentionally; I tend to prefer keeping things private and using setters, but meh. – Dave Newton Jan 02 '12 at 15:32
  • actually my UserServiceImpl is in com.abc.services and my controller is in com.abc.web.controllers and my unit test for the controller is in com.abc.web.controllers so that is why it works for my controller and not service right? makes sense now. – Blankman Jan 02 '12 at 15:33
  • Yes so that is what I did, I set all those injected Dao's etc. in my UserService with setters. I don't like writing code just to make testing easier..oh well. I guess the other way would be to use injection in my testing somehow. – Blankman Jan 02 '12 at 15:34
  • 1
    @Blankman: You should annotate the setters with Autowired to make Spring use setter injection instead or field injection. Or, as I even prefer, you should use constructor injection and annotate the constructor with Autowired. This makes all the dependencies obvious, allows for final fields and immutable services, eases refactoring, and doesn't need setters. – JB Nizet Jan 02 '12 at 15:53
  • final fields are good for what? performance? – Blankman Jan 02 '12 at 21:44

3 Answers3

5

Spring framework has very interesting features for testing. You can take a look at Spring reference guide. It can provide DI even in your JUnit test class.

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/applicationContext.xml" and "/applicationContext-test.xml"
// in the root of the classpath
@ContextConfiguration(locations={"/applicationContext.xml", "/applicationContext-test.xml"})
public class MyTest {
    // class body...
}

Briefly, you can use your own applicationContext.xml or even define a new one just for testing. I personally use a different one since I define another dataSource dedicated for testing purposes.

Jon Onstott
  • 13,499
  • 16
  • 80
  • 133
Trein
  • 3,658
  • 27
  • 36
4

What I find strange is that for my spring controller (that doesn't implement from Controller) I was able to access my private varialbe to manually set my service, i.e:

This is easy: the varible in not private.

It has the default visibility ("package private"). This mean you can access them from all classes of the same package.

So if you have a common structure, then the controller and the controller test case are in the same package. Therefore you can modify the ("package private") controller fields. But the controller test case and the service are not in the same packaged, so you can not access the ("package private") service fields.

Ralph
  • 118,862
  • 56
  • 287
  • 383
1

Can I re-use the spring container's xml file that has all my beans etc. or do I create a new one? (I'm assuming I have to get the spring container involved here)

I would advice against creating new xml file. You would end up duplicating lot of stuff and its going to be hard to maintain. There would be proliferation of config files. You put the config required for tests in a different xml and that should not even be deployed to your production box. So far as using the config for your beans, you can employ the mechanism as suggested by @Trein.

For testing spring contrller in general, you may also find this SO Thread useful.

Hope that helps.

Community
  • 1
  • 1
Nilesh
  • 4,137
  • 6
  • 39
  • 53