Python/Jython Unittest

is it possible to use the standard Python/Jython unittest modules with Ignition? My goal is to [eventually] incorporate these unittests in with Jenkins and potentially then EAM, but right now I’m struggling to get even a trivial example to work inside the script console.

The error message I’m receiving:

Traceback (most recent call last):
  File "<buffer>", line 72, in <module>
  File "C:\Users\<username>\.ignition\cache\<gwname>_8088_443_main\C0\pylib\", line 766, in __init__
    self.progName = os.path.basename(argv[0])
IndexError: index out of range: 0

The code I’m attempting to run, pulled from here:

class Person:
    name = []

    def set_name(self, user_name):
        return len( - 1

    def get_name(self, user_id):
        if user_id >= len(
            return 'There is no such user'

import unittest

class Test(unittest.TestCase):
    The basic class that inherits unittest.TestCase
    person = Person()  # instantiate the Person Class
    user_id = []  # variable that stores obtained user_id
    user_name = []  # variable that stores person name

    # test case function to check the Person.set_name function
    def test_0_set_name(self):
        print("Start set_name test\n")
        Any method which starts with ``test_`` will considered as a test case.
        for i in range(4):
            # initialize a name
            name = 'name' + str(i)
            # store the name into the list variable
            # get the user id obtained from the function
            user_id = self.person.set_name(name)
            # check if the obtained user id is null or not
            self.assertIsNotNone(user_id)  # null user id will fail the test
            # store the user id to the list
        print("user_id length = ", len(self.user_id))
        print("user_name length = ", len(self.user_name))
        print("\nFinish set_name test\n")

    # test case function to check the Person.get_name function
    def test_1_get_name(self):
        print("\nStart get_name test\n")
        Any method that starts with ``test_`` will be considered as a test case.
        length = len(self.user_id)  # total number of stored user information
        print("user_id length = ", length)
        print("user_name length = ", len(self.user_name))
        for i in range(6):
            # if i not exceed total length then verify the returned name
            if i < length:
                # if the two name not matches it will fail the test case
                self.assertEqual(self.user_name[i], self.person.get_name(self.user_id[i]))
                print("Testing for get_name no user test")
                # if length exceeds then check the 'no such user' type message
                self.assertEqual('There is no such user', self.person.get_name(i))
        print("\nFinish get_name test\n")

if __name__ == '__main__':
    # begin the unittest.main()
Traceback (most recent call last):
  File "&lt;buffer&gt;", line 72, in &lt;module&gt;
  File "C:\Users\&lt;username&gt;\.ignition\cache\&lt;gwname&gt;_8088_443_main\C0\pylib\", line 766, in __init__
    self.progName = os.path.basename(argv[0])
IndexError: index out of range: 0

This (along with if __name__ == '__main__':) implies that the unittest module assumes it’s running directly from the command line. Unfortunately, that’s true of nowhere in Ignition - in all cases, including the script console, the “raw” script you write is passed off to a Java method that compiles and executes the code, passing in particular values for Python’s locals() and globals(). At a guess, the unittest module may work work in a pure Jython environment (which is why it’s included in our distribution of the standard library) but you would probably have to do some significant re-engineering to get it to work within an Ignition environment.

1 Like

Thanks, I appreciate it. From the script console I was able to get an alternative calll working, as I suspected something along the lines of command line/main call.

import unittest

def fib(n):
	""" Calculates the n-th Fibonacci number iteratively 
	>>> fib(0)
	>>> fib(1)
	>>> fib(10) 
	>>> fib(40)
	a, b = 0, 1
	for i in range(n):
		a, b = b, a + b
	return a

class FibonacciTests(unittest.TestCase):
    def test_Dummy(self):
    def test_Calculation(self):
        self.assertEqual(fib(0), 0)
        self.assertEqual(fib(1), 0)
        self.assertEqual(fib(5), 5)
        self.assertEqual(fib(10), 55)
        self.assertEqual(fib(20), 6765)

suite = unittest.TestLoader().loadTestsFromTestCase(FibonacciTests)
output = unittest.TextTestRunner(verbosity=2).run(suite)

if output.wasSuccessful():
	print 'Passed tests.'
	number_failed = len(output.failures) + len(output.errors)
	print "Failed "+str(number_failed)+ " test(s)"
1 Like

Hi tim,

When I code that’s big enough to warrant testing, I usually develop it as a separate library. See

Then it’s possible to use unit testing from the console or in an IDE (though tooling support for Jython 2.5 is quite bad at the moment).


Thanks Sander - that is a good idea. May I pick your brain a bit - how do you decide when code is ‘big enough’? Just trying to get a feel for the dev ops and QA side of Ignition development vs traditional development, and some best practices.

1 Like

If it’s a stand-allone function, and the effects are immediately visible in the client, I usually don’t test it since it’s easy enough to confirm the validity of the function.

With stand-allone, I mean the data comes either from the global scope (tags), or from visible client state, and the function only gets called in a specific context.

When functions get linked together, or use data that’s hard to consult, it usually makes debugging harder, so I put it in a library and write tests for it. To me, it’s not about the single function being small or large, it’s about it being isolated or not.

1 Like