The application is written by kivy.
I want to test a function via pytest, but in order to test that function, I need to initalize the object first, but the object needs something from the UI when initalizing, but I am at testing phase, so don't know how to retrieve something from the UI.
This is the class which has an error and has been handled
def check_phone_number_on_first_contact(self, button):
s = self.instanciate_ServerMsg(tt)
except HTTPError as err:
print("[HTTPError] : " + str(err.code))
# some code when running without error
#send something to server via urllib.urlopen
myError = HTTPError(url="http://127.0.0.1", code=500,
msg="HTTP Error Occurs", hdrs="donotknow", fp=None)
mockServerMsg = mock.Mock(spec=ServerMsg)
mockServerMsg.send.side_effect = myError
sc = SaltConfig(ds_config_file_missing.data_store)
monkeypatch.setattr(sc, 'instanciate_ServerMsg', mockreturn)
I made an article about testing Kivy apps together with a simple runner - KivyUnitTest. It works with
unittest, not with
pytest, but it shouldn't be hard to rewrite it, so that it fits your needs. In the article I explain how to "penetrate" the main loop of UI and this way you can happily go and do with button this:
button = <button you found in widget tree> button.dispatch('on_release')
and many more. Basically you can do anything with such a test and you don't need to test each function independently. I mean... it's a good practice, but sometimes (mainly when testing UI), you can't just rip the thing out and put it into a nice 50-line test.
This way you can do exactly the same thing as a casual user would do when using your app and therefore you can even catch issues you'd have trouble with when testing the casual way e.g. some weird/unexpected user behavior.
Here's the skeleton:
import unittest import os import sys import time import os.path as op from functools import partial from kivy.clock import Clock # when you have a test in <root>/tests/test.py main_path = op.dirname(op.dirname(op.abspath(__file__))) sys.path.append(main_path) from main import My class Test(unittest.TestCase): def pause(*args): time.sleep(0.000001) # main test function def run_test(self, app, *args): Clock.schedule_interval(self.pause, 0.000001) # Do something # Comment out if you are editing the test, it'll leave the # Window opened. app.stop() def test_example(self): app = My() p = partial(self.run_test, app) Clock.schedule_once(p, 0.000001) app.run() if __name__ == '__main__': unittest.main()
However, as Tomas said, you should separate UI and logic when possible, or better said, when it's an efficient thing to do. You don't want to mock your whole big application just to test a single function that requires communication with UI.