[yast-devel] Webyast: HttpMock and utility for it
Hi, in short: in branch for yast model I add also injector to HttpMock (standard library which is part of ActiveResource to test Rest-service based models) so if you plan write test look at it (or wait until it is merged). in long: Main goal to add models to webclient instead of dynamic one is easier testing of functionality which should go to model as controller is only thin layer which serves data. But I found that it is not so easy due to our design and also not so simple situation with rest-service site data. From my point of view correct testing of rest-service is provide same data as expected from rest-service and test if it work as expected. Now if test is ever present (not much models, I already fill bug) then it is huge mock which in real doesn't test much functionality. So we need some mock service which mock only target rest-service. Why I choose HttpMock? Even if our local consultant suggest use another framework I choose http because it looks like sufficient for our goal and it is already in ActiveResource so we don't need another gems. Why not use HttpMock without utilities? Of course it is possible, but you must simulate resource which translate interface to path and also permissions service which is out of test focus and test should not contain permissions service details. Also there is problem with authentication which must be solved on your own. So I create injector which add useful methods to HttpMock. How to use it in test: 1) define response (we separate at least visually tested data and tests): TEST_RESPONSE = <<EOF <test> <arg1>test</arg1> </test> EOF 2) now mock network service ( I suggest use it in setup - but of course for testing exceptions you should redefine it in method) def setup ActiveResource::HttpMock.set_authentification #a) ActiveResource::HttpMock.respond_to do |mock| header = ActiveResource::HttpMock.authentification_header #b) mock.resources :'org.opensuse.yast.modules.test' => "/test", :'org.opensuse.yast.modules.test2' => "/test2" #c) mock.permissions "org.opensuse.yast.modules.test", { :synchronize => true } #d) mock.get "/test.xml", header, TEST_RESPONSE, 200 mock.post "/test.xml", header, TEST_RESPONSE, 200 mock.get "/test2.xml", header, TEST2_RESPONSE, 200 mock.post "/test2.xml", header, TEST2_RESPONSE, 200 end end a) this utility sets authentification and target machine to predefined values b) authentification_header get hash which is passed to authenticate. It is required in mock header. otherwise it is not matched, but it is hash, so you can add your own values. c) define resources ( often it is just one, but for example if you use two rest-service (as time use time and ntp) then you define both. after hash argument you pass hash with options, which contains key singleton and policy ( mentioned in previous mail why I found it useless so it is optional, without it I use default which is no policy and single resource) d) mock permissions controller, just pass prefix and then hash with permission and if it is granted rest of method is common HttpMock which return expected values, so you can freely test it. 3) test model or controller :) def test_find_and_save test = TestModel.find :one assert_equal "test",test.arg1 #see XML for arg1 value assert test.save end Note: This utility class can be used without yast_model base model, because also dynamic created ActiveResource use this resources (but I do not test it, so expect problems) If you don't have any complains or suggestion how to improve it I plan to merge it together with yast_model. welcome any questions, suggestions or complains Josef -- Josef Reidinger YaST team maintainer of perl-Bootloader, YaST2-Repair, webyast (language,time,basesystem,ntp) -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org For additional commands, e-mail: yast-devel+help@opensuse.org
Hi, I am not sure if this is the right way to "simulate" the Rest service. I would prefer "sinatra" which is already used in the UI tests. This is quite a simple REST-API emulation which emulates the service in easy way. Have a look to web-client/webclient/test/dummy-host So, we would describe the REST service in one place only. This dummy-service could be used for - UI tests with selenium These kind of tests will become more important due the increasing use of AJAX. - WebClient test with the Rails test envirionment - Developping WebClient modules without having a real REST-service in the background. My fear is that we have too much test environments and no one is up to date or fits all requirements. Greetings Stefan Josef Reidinger schrieb:
Hi, in short: in branch for yast model I add also injector to HttpMock (standard library which is part of ActiveResource to test Rest-service based models) so if you plan write test look at it (or wait until it is merged).
in long: Main goal to add models to webclient instead of dynamic one is easier testing of functionality which should go to model as controller is only thin layer which serves data. But I found that it is not so easy due to our design and also not so simple situation with rest-service site data. From my point of view correct testing of rest-service is provide same data as expected from rest-service and test if it work as expected. Now if test is ever present (not much models, I already fill bug) then it is huge mock which in real doesn't test much functionality. So we need some mock service which mock only target rest-service. Why I choose HttpMock? Even if our local consultant suggest use another framework I choose http because it looks like sufficient for our goal and it is already in ActiveResource so we don't need another gems. Why not use HttpMock without utilities? Of course it is possible, but you must simulate resource which translate interface to path and also permissions service which is out of test focus and test should not contain permissions service details. Also there is problem with authentication which must be solved on your own. So I create injector which add useful methods to HttpMock.
How to use it in test: 1) define response (we separate at least visually tested data and tests):
TEST_RESPONSE = <<EOF <test> <arg1>test</arg1> </test> EOF
2) now mock network service ( I suggest use it in setup - but of course for testing exceptions you should redefine it in method) def setup ActiveResource::HttpMock.set_authentification #a) ActiveResource::HttpMock.respond_to do |mock| header = ActiveResource::HttpMock.authentification_header #b) mock.resources :'org.opensuse.yast.modules.test' => "/test", :'org.opensuse.yast.modules.test2' => "/test2" #c) mock.permissions "org.opensuse.yast.modules.test", { :synchronize => true } #d) mock.get "/test.xml", header, TEST_RESPONSE, 200 mock.post "/test.xml", header, TEST_RESPONSE, 200 mock.get "/test2.xml", header, TEST2_RESPONSE, 200 mock.post "/test2.xml", header, TEST2_RESPONSE, 200 end end
a) this utility sets authentification and target machine to predefined values b) authentification_header get hash which is passed to authenticate. It is required in mock header. otherwise it is not matched, but it is hash, so you can add your own values. c) define resources ( often it is just one, but for example if you use two rest-service (as time use time and ntp) then you define both. after hash argument you pass hash with options, which contains key singleton and policy ( mentioned in previous mail why I found it useless so it is optional, without it I use default which is no policy and single resource) d) mock permissions controller, just pass prefix and then hash with permission and if it is granted
rest of method is common HttpMock which return expected values, so you can freely test it.
3) test model or controller :) def test_find_and_save test = TestModel.find :one assert_equal "test",test.arg1 #see XML for arg1 value assert test.save end
Note: This utility class can be used without yast_model base model, because also dynamic created ActiveResource use this resources (but I do not test it, so expect problems)
If you don't have any complains or suggestion how to improve it I plan to merge it together with yast_model.
welcome any questions, suggestions or complains Josef
-- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org For additional commands, e-mail: yast-devel+help@opensuse.org
Hi, I saw your sinatra rest simulation and there is few problems and few advantages: Sinatra is light web framework which in your cases serve static files (defined as constants). 1) It is not easy to change content of response to test corner cases and error cases 2) it consume some time and imprecise speed measures for running tests Advantage is that it runs on target port, so it works good with browser based tests which doesn't touch network communication and that is what HttpMock cannot do. I think that all out targets cannot be solve with one framework. But we can share data format so in case we change format on rest-service we change it in one place. So what I do in HttpMock for testing is not static initialized string, but I have fixtures where is xml file with response and if we create convention for naming this fixtures, sinatra also can load it and use correct case in its tests ( convention like time.xml is working fixtures time-error.xml is saving error and other time-*.xml is different corner case and error cases ). Then in HttpMock you use: response_time = IO.read(File.join(File.dirname(__FILE__),"..","fixtures","time.xml")) response_ntp = IO.read(File.join(File.dirname(__FILE__),"..","fixtures","ntp.xml")) ActiveResource::HttpMock.set_authentification ActiveResource::HttpMock.respond_to do |mock| header = ActiveResource::HttpMock.authentification_header mock.resources :"org.opensuse.yast.modules.yapi.time" => "/systemtime", :"org.opensuse.yast.modules.yapi.ntp" => "/ntp" mock.permissions "org.opensuse.yast.modules.yapi.time", { :read => true, :write => true } mock.permissions "org.opensuse.yast.modules.yapi.ntp", { :available => true, :synchronize => true } mock.get "/systemtime.xml", header, response_time, 200 mock.post "/systemtime.xml", header, response_time, 200 mock.get "/ntp.xml", header, response_ntp, 200 mock.post "/ntp.xml", header, response_ntp, 200 end and sinatra or other static serving server can just search through plugins fixtures and serves files. What each framework must do is define permissions (typical sinatra grant all and tests play with it) and own resources as it can changes and in test should be tested if some part missing (e.g. ntp for time plugin). For developing webclient I use rest-service at virtual machine as I often test only view with it. Remaining think if you use test-driven development should be tested. ( If something doesn't work, that also tests must fail ) Josef Stefan Schubert write:
Hi, I am not sure if this is the right way to "simulate" the Rest service. I would prefer "sinatra" which is already used in the UI tests. This is quite a simple REST-API emulation which emulates the service in easy way. Have a look to web-client/webclient/test/dummy-host So, we would describe the REST service in one place only. This dummy-service could be used for
- UI tests with selenium These kind of tests will become more important due the increasing use of AJAX. - WebClient test with the Rails test envirionment - Developping WebClient modules without having a real REST-service in the background.
My fear is that we have too much test environments and no one is up to date or fits all requirements.
Greetings Stefan
Josef Reidinger schrieb:
Hi, in short: in branch for yast model I add also injector to HttpMock (standard library which is part of ActiveResource to test Rest-service based models) so if you plan write test look at it (or wait until it is merged).
in long: Main goal to add models to webclient instead of dynamic one is easier testing of functionality which should go to model as controller is only thin layer which serves data. But I found that it is not so easy due to our design and also not so simple situation with rest-service site data. From my point of view correct testing of rest-service is provide same data as expected from rest-service and test if it work as expected. Now if test is ever present (not much models, I already fill bug) then it is huge mock which in real doesn't test much functionality. So we need some mock service which mock only target rest-service. Why I choose HttpMock? Even if our local consultant suggest use another framework I choose http because it looks like sufficient for our goal and it is already in ActiveResource so we don't need another gems. Why not use HttpMock without utilities? Of course it is possible, but you must simulate resource which translate interface to path and also permissions service which is out of test focus and test should not contain permissions service details. Also there is problem with authentication which must be solved on your own. So I create injector which add useful methods to HttpMock.
How to use it in test: 1) define response (we separate at least visually tested data and tests):
TEST_RESPONSE = <<EOF <test> <arg1>test</arg1> </test> EOF
2) now mock network service ( I suggest use it in setup - but of course for testing exceptions you should redefine it in method) def setup ActiveResource::HttpMock.set_authentification #a) ActiveResource::HttpMock.respond_to do |mock| header = ActiveResource::HttpMock.authentification_header #b) mock.resources :'org.opensuse.yast.modules.test' => "/test", :'org.opensuse.yast.modules.test2' => "/test2" #c) mock.permissions "org.opensuse.yast.modules.test", { :synchronize => true } #d) mock.get "/test.xml", header, TEST_RESPONSE, 200 mock.post "/test.xml", header, TEST_RESPONSE, 200 mock.get "/test2.xml", header, TEST2_RESPONSE, 200 mock.post "/test2.xml", header, TEST2_RESPONSE, 200 end end
a) this utility sets authentification and target machine to predefined values b) authentification_header get hash which is passed to authenticate. It is required in mock header. otherwise it is not matched, but it is hash, so you can add your own values. c) define resources ( often it is just one, but for example if you use two rest-service (as time use time and ntp) then you define both. after hash argument you pass hash with options, which contains key singleton and policy ( mentioned in previous mail why I found it useless so it is optional, without it I use default which is no policy and single resource) d) mock permissions controller, just pass prefix and then hash with permission and if it is granted
rest of method is common HttpMock which return expected values, so you can freely test it.
3) test model or controller :) def test_find_and_save test = TestModel.find :one assert_equal "test",test.arg1 #see XML for arg1 value assert test.save end
Note: This utility class can be used without yast_model base model, because also dynamic created ActiveResource use this resources (but I do not test it, so expect problems)
If you don't have any complains or suggestion how to improve it I plan to merge it together with yast_model.
welcome any questions, suggestions or complains Josef
-- Josef Reidinger YaST team maintainer of perl-Bootloader, YaST2-Repair, webyast (language,time,basesystem,ntp) -- To unsubscribe, e-mail: yast-devel+unsubscribe@opensuse.org For additional commands, e-mail: yast-devel+help@opensuse.org
participants (2)
-
Josef Reidinger
-
Stefan Schubert