Craig Ambrose
2005-Aug-29 22:01 UTC
Controllers have no session object when you construct them, so how can I test the flash?
Hi folks, I''m attempting to _unit_ test a controller. This may sound a little wacky to those who only perform functional tests on controllers, but I find that it''s much easier to do, runs much faster, and better suits my XP style development methodology. Also (and very importantly in my case), it seems to be just about the only way to test controllers used in components. Anyway, it''s been going very well, until suddenly I want to use the flash. Because I constructed my own controller, it appears to have no session object, and thus call to it''s "flash" method ends up indexing null. For example, consider the controller. And test below. -------------------------------------------------------------------- class Blog::ComponentController < ActionController::Base def edit flash[''notice''] = ''performing edit action'' end end class BlogControllerTest < Test::Unit::TestCase def test_edit_does_something controller = Blog::ComponentController.new controller.edit end end -------------------------------------------------------------------- In this case, test_edit_does_something will call controller.edit, which will cause it an exception when it tries to use the flash. In controller functional tests I would normally call "get" or "post" in order the power up the whole rails framework and get it to use the controller. I''ve spent a couple of hours pouring through that code, and I can''t figure out how the controller''s session get''s initialised during that process, or what I can do myself to fake it. Any ideas would be much appreciated. Craig
Craig Ambrose
2005-Aug-30 09:48 UTC
Controllers have no session object when you construct them, so how can I test the flash?
Hi folks, I''m attempting to _unit_ test a controller. This may sound a little wacky to those who only perform functional tests on controllers, but I find that it''s much easier to do, runs much faster, and better suits my XP style development methodology. Also (and very importantly in my case), it seems to be just about the only way to test controllers used in components. Anyway, it''s been going very well, until suddenly I want to use the flash. Because I constructed my own controller, it appears to have no session object, and thus call to it''s "flash" method ends up indexing null. For example, consider the controller. And test below. -------------------------------------------------------------------- class Blog::ComponentController < ActionController::Base def edit flash[''notice''] = ''performing edit action'' end end class BlogControllerTest < Test::Unit::TestCase def test_edit_does_something controller = Blog::ComponentController.new controller.edit end end -------------------------------------------------------------------- In this case, test_edit_does_something will call controller.edit, which will cause it an exception when it tries to use the flash. In controller functional tests I would normally call "get" or "post" in order the power up the whole rails framework and get it to use the controller. I''ve spent a couple of hours pouring through that code, and I can''t figure out how the controller''s session get''s initialised during that process, or what I can do myself to fake it. Any ideas would be much appreciated. Craig
Adam Williams
2005-Aug-31 12:16 UTC
Re: Controllers have no session object when you construct them, so how can I test the flash?
On Aug 29, 2005, at 6:01 PM, Craig Ambrose wrote:> Because I constructed my own controller, it appears to have no > session object, and thus call to it''s "flash" method ends up > indexing null. > ... > In this case, test_edit_does_something will call controller.edit, > which will cause it an exception when it tries to use the flash. In > controller functional tests I would normally call "get" or "post" > in order the power up the whole rails framework and get it to use > the controller. I''ve spent a couple of hours pouring through that > code, and I can''t figure out how the controller''s session get''s > initialised during that process, or what I can do myself to fake it. > > Any ideas would be much appreciated.I look forward to hearing other responses to this... I have been working with Sails, which has the same challenge. When I began months ago, I wanted to test Controllers just as you described: invoke the action directly. Sails has everything needed to mock the HttpServletRequest and HttpServletResponse, so it seemed the natural thing to do. It quickly became a nightmare to maintain. Think about your action: it has an instance variable for a request, which I would suppose is coming from ActionController::Base or being added at some other point. By the time your action is invoked, it''s all there. If you unit test as you describe, something needs to set all that up. What I did in Sails, at first, was create a ControllerTestCase that could be sub-classed: it provided a ''createController'' method. This had to effectively duplicate whatever was happening in the framework to create an instance of a Controller and initialize it''s context. This became especially burdensome to the developer when the Controller had dependencies, which are being met through Inversion of Control. How could they easily inject mocks? More challenging: how could they NOT inject mocks - use the components defined for application runtime because they are the best choice - without have to go back to every test class when you add new components, or, worse, duplicate something? Anyway, I ended up doing what Rails does. Execute as much of the real code as possible, replacing key pieces with fakes that make testing easier. Its feeling more and more like Rails ''functional'' tests. Looking back, I think my desire to do the testing ''unit'' style was well motivated. In reality, it provided very little value. That said, if there is a good way... Hopefully Helpful, adam williams