Limitations of Python modules for unit testing?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
boundsoul
Newbie
Posts: 11
Joined: Wed Feb 02, 2022 5:38 pm
Contact:

Limitations of Python modules for unit testing?

#1 Post by boundsoul »

I've been looking all over the forum, the documentation, and Google to see how people handle tests in Ren'Py.

The answers that come up are either:
  • You can manually test it
This isn't great, and it can get time-consuming if you have to test complex logic.
  • You can use the testcase system
This seems to be intended for GUI tests. It's also not really documented beyond the tutorial project, so I'm kind of reluctant to use it, since it feels like the kind of feature that might not be fully supported and might change at a moment's notice.

I could be wrong, though.
  • You can write your logic as Python code (in a .py file) and import the module into your Ren'Py scripts, while writing your tests outside of Ren'Py
This last one kind of puzzles me, because the documentation has multiple warnings about importing Python modules into Ren'Py scripts. It really emphasizes how Ren'Py will transform code written inside an .rpy file so that it works with features like serialization (save, load, rollback), and that Python code that's imported into Ren'Py won't be transformed to work with those features.

After seeing a few posts about it, I figured the documentation might be outdated, so I went ahead and wrote a Python module with a simple data class, imported it into my Ren'Py scripts, and tried doing basic save, load, and rollback operations, but it resulted in broken data (just like the docs suggested).

Here's the class, for those curious (there's nothing special about it):

Code: Select all

# Python .py file
class DataObject:
    def __init__(self):
        self.a = 0
        self.b = 0

Code: Select all

# Ren'Py .rpy file
init python:
    from MyModule.MyFile import DataObject
    
default data = DataObject()
If I move the class definition into the .rpy file directly, the save, load, and rollback features all work as expected. As far as I can tell, the documentation's warnings are accurate.

---

So, I just want to make sure I fully understand the limitations of using Python modules inside Ren'Py files here (and that I'm not just missing something obvious):
  • If I want to write unit tests, I need to use external Python modules (because there's no way to test Ren'Py code using testing libraries like pytest).
  • I can't define "data" classes in Python modules, because they won't work with Ren'Py's serialization features. I need to define "data" classes in Ren'Py script files.
  • I can define "pure logic" classes/functions in Python modules, and use define to instantiate the classes in my Ren'Py scripts (since they're not supposed to store any data).
  • Because of the previous points, I'll need to pass my "data" classes to my "logic" classes' functions, and make sure they don't store any references for future use.
  • Because my "data" classes are defined in my Ren'Py code, I'll need to mock/stub the "data" classes in my unit tests (since I can't import code from an .rpy file into a .py file).
Is my assessment of the situation accurate or am I missing something?

I'd rather not go all in on this and learn that I've been missing something this whole time, haha.

User avatar
Tess
Regular
Posts: 59
Joined: Thu Aug 04, 2022 3:43 pm
Projects: The Songbird Guild
Organization: Yurisoft
Github: wainwt2
Discord: Tess#7782
Contact:

Re: Limitations of Python modules for unit testing?

#2 Post by Tess »

For testing python code in rpy files, I like to use Ren'Py's lint features. You can add a test function to config.lint_hooks to have it run when you lint your scripts, and any print statements will be added to the output.

I have a decorator that handles it in my code to make things easier:

Code: Select all

init -99 python:
	def lint_test(test):
		config.lint_hooks.append(test)
Then you can just add @lint_test to your test functions:

Code: Select all

python:
	def addition(x,y):
		return x+y
	
	@lint_test
	def test_addition():
		if addition(1,2) != 3:
			print "Something has gone terribly wrong."
I haven't found a super eloquent way to use it with labels yet, but I'm sure it's possible.
I also haven't found a good way to debug screens, but that testcase thing you mentioned seems interesting, I'll have to look into it.

Probably not everything you were looking for but maybe it could be a step in the right direction! :D

boundsoul
Newbie
Posts: 11
Joined: Wed Feb 02, 2022 5:38 pm
Contact:

Re: Limitations of Python modules for unit testing?

#3 Post by boundsoul »

Tess wrote: Tue Jan 10, 2023 3:02 pm For testing python code in rpy files, I like to use Ren'Py's lint features. You can add a test function to config.lint_hooks to have it run when you lint your scripts, and any print statements will be added to the output.
I wasn't aware we could add methods to the linter, that could be interesting.

The issue that immediately comes to mind is that this probably bundles the tests inside the final release, no? I'd expect so, since they're defined in .rpy files. Hmm...

Tess wrote: Tue Jan 10, 2023 3:02 pm I haven't found a super eloquent way to use it with labels yet, but I'm sure it's possible.
I also haven't found a good way to debug screens, but that testcase thing you mentioned seems interesting, I'll have to look into it.
You can find an example in the Tutorial project, it's under the Testcases.ryp file.

This post has more info on them: viewtopic.php?f=8&t=64740&p=553021&hili ... se#p553021

I've been playing around with them yesterday, and it's a bit bare bones. I had to make some changes to the tests to get them to run properly, too (seems Tutorial no longer ships with the Roboto-Regular font, for example, which causes the tests to fail). I'm also not 100% sure how to get them to click on UI elements that don't have strings on them (haven't gotten around to testing that part out).

I'm pretty sure it's either not ready or not intended for serious use right now, especially since it's not really a documented feature.

User avatar
Tess
Regular
Posts: 59
Joined: Thu Aug 04, 2022 3:43 pm
Projects: The Songbird Guild
Organization: Yurisoft
Github: wainwt2
Discord: Tess#7782
Contact:

Re: Limitations of Python modules for unit testing?

#4 Post by Tess »

boundsoul wrote: Tue Jan 10, 2023 4:14 pm The issue that immediately comes to mind is that this probably bundles the tests inside the final release, no? I'd expect so, since they're defined in .rpy files. Hmm...
You can use build.classify to prevent your test folder from being included in the build. For example:

Code: Select all

init python:
	build.classify("game/Tests/**","None")
tells Ren'Py not to include anything from game/Tests

There's more information on this page: https://www.renpy.org/doc/html/build.html

Post Reply

Who is online

Users browsing this forum: cruisy__