RSpec verifying doubles and better YaST module mocking
Hi! I have enabled RSpec verifying doubles in all remaining YaST unit tests where they were not used. The advantage is that RSpec checks that the mocked methods really exist in the original class. Without it if you make a typo in a method call in the code and make the same typo in the unit test mock then the test will pass but the code will actually crash at run time. So ideally we should enable this feature everywhere. But in the YaST unit test we quite often replace some YaST modules with an empty class to avoid too long or cyclic build dependencies. If you mock an YaST module completely then you have the same problem back as the dummy methods are really defined there so even RSpec verifying doubles will not catch the problems there. To use advantages from the both worlds I have created a new Yast::RSpec::Helpers.define_yast_module helper method. [1] It tries to load the requested YaST module via the usual YaST.import call. If that fails then it defines a dummy replacement for it. So if the module is present in the system, like when you run the unit tests locally on your machine or when running in GitHub Actions where most of the modules are present, then it will load the real modules and the verifying doubles will fully work. In RPM build it will create dummy modules and the build dependencies can be avoided. Examples: # defined here, needs yast2-ruby-bindings >= 4.4.7 require "yast/rspec" # if you do not mock the module methods in the tests you can use # this simple variant Yast::RSpec::Helpers.define_yast_module("AutoInstall") # if you mock some methods then they must be defined in the mocked class # so the verifying doubles check does not fail Yast::RSpec::Helpers.define_yast_module("Profile", methods: [:current]) # note: for simplification all mocked methods accept any number of parameters, # the real parameters should be verified when running against the real modules # you can also pass a block which is evaluated in the context of the created class # so you can define some defaults, in theory you could define also some logic there # but I'd avoid it, the mocked logic might easily go out of sync with the real module Yast::RSpec::Helpers.define_yast_module("Language") do def language # the default "en_US" end end Then if you run "rake test:unit" locally the modules will be loaded from system when found. If you want to force creating the mocked modules even when they are present in the system then just set the YAST_FORCE_MODULE_STUBS environment variable, e.g.: YAST_FORCE_MODULE_STUBS=1 rake test:unit This is useful if you want to test locally how it will work in RPM build, whether your mocks implement all the methods used in the tests. You can check some real examples in [2] and [3]. And interestingly enabling verifying doubles found a real bug in the code! [4] Enjoy! Ladislav [1] https://github.com/yast/yast-ruby-bindings/pull/281 [2] https://github.com/yast/yast-bootloader/pull/663/files [3] https://github.com/yast/yast-network/pull/1278/files [4] https://github.com/yast/yast-installation/pull/1013/files#diff-05183469b1a05... -- Ladislav Slezák YaST Developer SUSE LINUX, s.r.o. Corso IIa Křižíkova 148/34 18600 Praha 8
participants (1)
-
Ladislav Slezák