Code Monkey home page Code Monkey logo

simple-testing-godot's Introduction

Simple Testing Godot

A small testing framework for the Godot 4 game engine that simplifies the process of creating and running unit tests within your Godot projects.

Features

  • Easy-to-use unit testing framework for GDScript projects
  • Intuitive interface displaying test results in a tree structure
  • SimpleError class for error handling
  • UnitTest class as a base for user-defined tests
  • Auto-refresh and auto-run feature to automatically update and run tests when changes are detected

Installation

Basic

  1. Download or clone this repository into the addons directory of your Godot 4 project.
  2. In the Godot Editor, navigate to Project > Project Settings > Plugins and enable the Simple Testing plugin.

Git Submodule

To add the plugin as a git submodule to your git project, follow these steps.

  1. Navigate to your Godot project directory in your terminal.
  2. Run the command git submodule add https://github.com/accmltr/simple-testing-godot.git addons/simple-testing-godot/.
  3. Commit the changes to your git repository by running the command git commit -m "Added Simple Testing plugin as a submodule.".
  4. Open your Godot project and go to Project/Project Settings/Plugins. Enable the "Simple Testing" plugin.
  5. Reload the Godot project by going to the Project menu and clicking on Reload Current Project.

Usage

Creating Unit Tests

  1. Create a new script that extends the UnitTest class provided by the plugin.
  2. Define your test functions with names starting with test_, such as test_example_function.
  3. Use the istrue function provided by the UnitTest class to assert conditions within your test functions.
  4. Use the error_happens function to check whether a block of code triggers an error as expected.

istrue

istrue has the following parameters:

  • condition (bool): The condition to be evaluated. If the condition is not true, an error will be logged.
  • msg (String): The error message to display if the condition is not true.
  • err_code (int, optional): An error code for the error, with a default value of -1.

Example usage:

istrue(result == 2, "Addition failed")

error_happens

error_happens has the following parameters:

  • code (Callable): A Callable object that represents the code block that is expected to trigger an error.
  • msg (String): The error message to display if the expected error does not occur.
  • err_code (int, optional): An error code for the error, with a default value of -1.
  • is_expected (Callable, optional): A Callable object that takes a SimpleError object as its argument and returns a boolean. The function should return true if the error passed to it is the expected error. If not provided, the function defaults to returning true for any error.

Example usage:

# without check callable:
var error_prone_code = func():
    istrue(0 == 10, "0 did not equal 10")
error_happens(error_prone_code, "The error prone code did not have any errors")

# with check callable:
var error_prone_code_2 = func():
    istrue(1 == 2, "1 did not equal 2", 8)

var check = func(e: SimpleError):
    return e.err_code == 8

error_happens(error_prone_code_2, "Expected error did not happen.", 1, check)

Running Unit Tests

  1. Open the Simple Testing dock within the Godot Editor.
  2. Click the Refresh button to rebuild the tree and display the available unit tests.
  3. Click the Run button to run all the unit tests and view the results in the tree structure.

Screenshot 2023-05-12 153452

Auto-Refresh and Auto-Run

  • Enable the Auto-Refresh checkbox to automatically rebuild the tree upon saving.
  • Enable the Auto-Run checkbox to automatically run tests after saving.

Autoloads

  • When using autoloads in your tests you need to annotate them with the@tool annotation at the top of the autoload scripts. This will make it possible for your testing code to access them when running from within the Godot Editor.
  • When making any changes to autoload scripts, you need to reload your Godot project for the changes to take effect. Since Godot compiles @tool scripts when loading the project. To reload your project, go to Project/Reload Current Project.

Note: We inted to improve the plugin by running all tests in play-mode in the future. This will remove the need for annotating autoloads used by your unit tests with @tool, and the need to reload your project whenever you make changes to your autoloads.

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests to help improve the Simple Testing Godot plugin.

License

This project is licensed under the MIT License. See the LICENSE file for more information.

simple-testing-godot's People

Contributors

accmltr avatar otizhi avatar

Stargazers

Jack avatar baihan avatar  avatar GameDevKnight avatar  avatar Samuel Lacroix avatar  avatar

Watchers

 avatar

Forkers

boyquotes

simple-testing-godot's Issues

Play mode tests doesn't fail when errors are generated by scripts outside of the PlayModeTest script

When a test calls a method on another object, and it registers an error that doesn't have the play-mode test's id set as its error source, the TestRunner doesn't collect the error and thus doesn't log that the play-mode test failed. The same happens when any error's are thrown without having the id of the current play mode test given as the error's source.

E.g, the following behaves correctly, and the play-mode test fails:

extends PlayModeTest

func _ready():
	throw_error("Example error.")
	queue_free()

image

but this code passes, when it should fail just the same:

extends PlayModeTest

func _ready():
	Testing.throw_error(self, "Example error.")
	queue_free()

image

Note: This issue belongs to the experimental branch! It specifically refers to this commit.

Run tests in play mode

Try to find a way to run tests in play mode, not in editor. This will:

  • Eliminate the need for user's to worry about @tool annotations in their autoloads.
  • Make it easy to create tests that can instance scenes and test those scenes while they are processing.
  • Make it possible for unexpected failed assertions pauses execution of tests (there should be a "Pause on Error" setting).
  • get_stack will work, since the built-in debugger will be active!

See #34 for rough overview of implementation.

UML Diagrams branch

Create diagrams to provide an overview of the project structure and design.

  • Sequence diagram for running tests
  • Class diagram of core classes in the project
  • Use case diagrams for various types of testing

When done, mention the Project UML Diagrams file as a good place to get more documentation on the plugin.

Remove `src` param from `istrue` and `error_happens` methods

Remove the src param in the methods of testing.gd and user_test.gd. Use the built in get_stack() method in Godot 4 instead of requiring users to provide a source for errors manually!

The constructor and src property in simple_error.gd will also need to be changed accordingly.

Update README to include note about adding `@tool` to user autoloads

Currently, if the user doesn't annotate their singletons with the @tool annotation, strange errors will happens if they try to use the public methods on those autoloads.

Users should be warned in the README to always add the annotation to their autoloads to ensure they work when being called by the testing framework.

User code not testable without `@tool`

In some cases, e.g. when testing user made singletons (autoloads), their code is unreachable and gives does not exist exceptions when trying to access the singelton's members.

Change `SimpleError` structure

Add a gen_msg property to the SimpleError class that may contain additional information provided by the Testing methods. This property serves as a way for the Testing methods to generate an additional error message alongside the error message written by the user.

For example, in the testing method error_happens, the gen_msg might contain the outputs of all errors that happened while running the code callable.

The gen_msg should be displayed in the dock as a child of the error's root TreeItem.

Note: This is mainly being added as a way to make creation of the expected testing method possible. This method is outlined in issue #24.

Remove unneccesary parameter In UnitTest's methods

The src param in the istrue(..) and error_happens(..) methods of the UnitTest class is not neccesary, since the source of the message can automatically specified by the method bodies when calling the actual implementations of these methods on the Testing singelton.

New testing method `expect(expected, found, msg="")`

Create a new testing method to go along with the existing assertions in the plugin, nl. istrue and error_happens.

This method will automatically generate a gen_msg containing a comparison between the expected and the found parameters.

Sample Usage

extends UnitTest

# Unit tests for the Transaction class.

func test_add():
	var a = Person.new("Person A")
	var b = Person.new("Person B")
	var t1 = Transaction.new(10, a, b)
	var t2 = Transaction.new(7, b, a)
	var expected = Transaction.new(3, a, b)
	var found = t1.add(t2)
	
	expect(expected, found, "Adding two transactions should result in a single transaction which can replace their effect.")

If the expect method should fail, the dock will display the user provided error message, along with the gen_error message. The gen_error in this case might look something like this:
expected: Transaction(3, a, b) found Transaction(3, b, a)

Make error messages optional

Make the err_msg param in the assertion methods of the plugin optional. This is for when users want to do many assertions consequtively inside a test without writing an error message for each.

Assertions like these without error messages should not be titled by their message in the dock, but by the assertion method used, and the line it was used at inside the unit test.

Improve sample tests for better demonstration of plugin usage

Introduction

The current sample code found in res://sample tests/ does not provide a good overview of how the plugin is meant to be used. The existing contents should be deleted, and new sub-directories named basic, intermediate, and advanced should be added. These directories should each contain a clear and easy-to-understand scenario in which the SimpleTesting plugin can be used, demonstrating the proper testing approach for the given scenario.

The code to be tested in each case should be saved in separate sub-directories called "scenario", e.g., res://sample tests/basic/scenario/, and the unit tests for that scenario can be saved in sub-directories called "tests", e.g., res://sample tests/basic/tests/.

Example scenario for sample tests/basic:

The sample tests/basic directory might contain a scenario with a user-created score system ../scenario/score.gd. The sample might then include a unit test ../tests/score_tests.gd that tests the functionality of the score system with a few straightforward examples of how to use the UnitTest.istrue() and UnitTest.error_happens() methods.

Further Requirements

The 'intermediate' and 'advanced' directories could contain more complex scenarios, such as verifying proper user data storage on the local system. Additional scenarios could involve instantiating scenes and performing operations on them, like loading the game world with the player character and testing their stun-immunity status effect or health regeneration rate. [Note: If testing scenes by running them is currently not possible, a new issue should be added to address this limitation].

Conclusion

These improvements will offer users a more comprehensive understanding of the plugin's capabilities and how to utilize it effectively in various situations. This will also serve as a way for beginners in testing to gain a better grasp of testing fundamentals.

Display `istrue` and `error_happens` usages even when assertions are successful

Display all the would-be error messages that istrue and error_happens methods would have generated if they failed under their respective method nodes in the dock tree. Display them in green to show that they've passed.

After implementing issue #6 - "Improve legibility of errors in the dock", the display of these green error messages should have the same format, simply green instead of red.

Note: This should be an optional setting!

Purpose

This will improve the testing dock by having it show all the assertions that did pass, to give more context when errors don't pass.

Change project root to be the current `res://addons/simple testing` dir

This way the repo can just be cloned into any project's res://addons directory for installation.

This would also make it easier to use this project as a git submodule in other git projects, which would be great for users and people trying to work on the plugin's development by testing it in various other projects.

Project will also have to be renamed to desired plugin folder name!

Dock output copy-paste

Add the option to select items in the dock and copy them as text. Also add quick "Copy All" button, or leave as copy dock hierarchy root.

This will make it easier to paste testing results into LLMs like ChatGPT.

Ensure all classes override built-in `_to_string` method

Some classes use a custom method name for generating a string representation of the class's data. Change any of those methods found to override the built-in _to_string method in Godot instead.

Just look through all the scripts to see if you can find any of these cases. If you've covered all scripts in the project, this issue may be closed.

Enable or disable testing for any directory, unit test, or `test_` method in dock

Making testing for any element and its children optional, will enable people to skip testing for certain parts of their projects which might take a long time to test.

Create a checkbox or visibility icon for each Tree Item and use it to enable or disable that item and its children (recursively) for testing. Also remember the user's settings by saving it to the user preferences file.

Improve legibility of errors in the dock

Do not display the entire error output in one item of the dock. Only display the error message and then add the rest of the error details as children to the message item in the tree.

Note: If possible, truncate error source string from the left, not the right. So the end of the source string is always visible.

Enexpected bug

Figure out why the code results in two unsuccessful tests

func test_add_with_non_statSheet():
	var s1 = StatSheet.new({Stat.CPOWER: 10})
	
	# Adding with a non-StatSheet object
	var non_statSheet = "This is a string, not a StatSheet"
	var error_prone_code = func():
		var s3 = s1.add(non_statSheet)
	error_happens(error_prone_code, "Adding with a non-StatSheet should raise an error")

	# Adding with a null object
	var null_object = null
	error_prone_code = func():
		var s3 = s1.add(null_object)
	error_happens(error_prone_code, "Adding with a null object should raise an error")

image

but the following is successful

func test_add_with_non_statSheet():
	var s1 = StatSheet.new({Stat.CPOWER: 10})
	
	# Adding with a non-StatSheet object
	var non_statSheet = "This is a string, not a StatSheet"
	var error_prone_code = func():
		var s3 = s1.add(non_statSheet)
	TestUtils.error_happens(error_prone_code, null, "Adding with a non-StatSheet should raise an error")

	# Adding with a null object
	var null_object = null
	error_prone_code = func():
		var s3 = s1.add(null_object)
	error_happens(error_prone_code, "Adding with a null object should raise an error")

image

Source: https://github.com/accmltr/Veiled/commit/0b6d4a7c24315f10cc5f33a86e9de454128a8569

Dock settings not being saved correctly

I noticed that the Auto run and Auto refresh toggles in the dock become re-enabled when reloading the project after turning them off.

They should load with the same states they were in when the project was last closed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.