From 5aae93e635618f836900ec10733c0f2b2fe1d642 Mon Sep 17 00:00:00 2001
From: Michael <michael@buchmann.ruhr>
Date: Tue, 10 Dec 2024 21:27:44 +0100
Subject: [PATCH] Added first example to plugin unit test (#8636)

* Start documentation chapter for plugin tests

* Added env variables to the doc

* Fix style errors

* Further style bugs

* Reformat environment variables

* Reformat environment variables

* Add comments from wolflu05

* Add text to the intro

* Added first example the plugin unit test

* Addred line it function

* Typo

* Typo

* Typo
---
 docs/docs/extend/plugins/test.md       |  81 +++++++++++++++++++++++++
 docs/mkdocs.yml                        |   1 +
 src/backend/InvenTree/.config.yaml.swp | Bin 16384 -> 0 bytes
 3 files changed, 82 insertions(+)
 create mode 100644 docs/docs/extend/plugins/test.md
 delete mode 100644 src/backend/InvenTree/.config.yaml.swp

diff --git a/docs/docs/extend/plugins/test.md b/docs/docs/extend/plugins/test.md
new file mode 100644
index 0000000000..26dfbe496e
--- /dev/null
+++ b/docs/docs/extend/plugins/test.md
@@ -0,0 +1,81 @@
+---
+Title: Unit Tests
+---
+
+## Unit Tests
+For complicated plugins it makes sense to add unit tests the code to ensure
+that plugins work correctly and are compatible with future versions too.
+You can run these tests as part of your ci against the current stable and
+latest tag to get notified when something breaks before it gets released as
+part of stable. InvenTree offers a framework for testing. Please refer
+to [Unit Tests](../../develop/contributing.md) for more information.
+
+### Prerequisites
+For plugin testing the following environment variables must be set to True:
+
+| Name | Function | Value |
+| --- | --- | --- |
+| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True |
+| INVENTREE_PLUGIN_TESTING | Enables enables all plugins no matter of their active state in the db or built-in flag | True |
+| INVENTREE_PLUGIN_TESTING_SETUP | Enables the url mixin | True |
+
+### Test program
+
+A file called test_plugin_name.py should be added to the plugin directory. It can have the
+following structure:
+
+```
+# Basic unit tests for the plugin
+from InvenTree.unit_test import InvenTreeTestCase
+
+class TestMyPlugin(InvenTreeTestCase):
+    def test_my_function(self):
+        do some work here...
+```
+
+The test can be executed using invoke:
+
+```
+invoke dev.test -r module.file.class
+```
+
+Plugins are usually installed outside of the InventTree directory, e.g. in .local/lib/...
+I that case module must be omitted.
+
+```
+invoke dev.test -r plugin_directory.test_plugin_name.TestMyPlugin
+```
+
+### do some work here... A simple Example
+A simple example is shown here. Assume the plugin has a function that converts a price string
+that comes from a supplier API to a float value. The price might have the form "1.456,34 €".
+It can be different based on country and local settings.
+The function in the plugin will convert it to a float 1456.34. It is in the class MySupplier
+and has the following structure:
+
+```
+class MySupplier():
+
+    def reformat_price(self, string_price):
+
+        ...
+        return float_price
+```
+
+This function needs to be tested. The test can look like this:
+
+```
+from .myplugin import MySupplier
+
+def test_reformat_price(self):
+
+    self.assertEqual(MySupplier.reformat_price(self, '1.456,34 €'), 1456.34)
+    self.assertEqual(MySupplier.reformat_price(self, '1,45645 €'), 1.45645)
+    self.assertEqual(MySupplier.reformat_price(self, '1,56 $'), 1.56)
+    self.assertEqual(MySupplier.reformat_price(self, ''), 0)
+    self.assertEqual(MySupplier.reformat_price(self, 'Mumpitz'), 0)
+```
+
+The function assertEqual flags an error in case the two arguments are not equal. In equal case
+no error is flagged and the test passes. The test function tests five different
+input variations. More might be added based on the requirements.
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index f341733627..86011269e5 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -199,6 +199,7 @@ nav:
       - Developing a Plugin: extend/how_to_plugin.md
       - Model Metadata: extend/plugins/metadata.md
       - Tags: extend/plugins/tags.md
+      - Unit Test: extend/plugins/test.md
     - Plugin Mixins:
       - Action Mixin: extend/plugins/action.md
       - API Mixin: extend/plugins/api.md
diff --git a/src/backend/InvenTree/.config.yaml.swp b/src/backend/InvenTree/.config.yaml.swp
deleted file mode 100644
index c9776e1c012b221ba59656cf75c03c7118dad53d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16384
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
Dy|To)