Unit Testing Wsadmin Jython Scripts

You can use the unittest module from Python to write unit testing for Websphere’s wsadmin Jython scripts. As of WASv61, this module comes shipped with the product so there is no need to refer externally to your CPython library.

To illustrate, let’s say you want to create a unittest for the Cell.py module below:

“Cell.py”

class Cell:
    def __init__(self, AdminControl=None):
        self.AdminControl = AdminControl

    def getName(self):
        return self.AdminControl.getCell()

Here’s a simple class that implements a method to retrieve the name of your Websphere cell.

To write a test case class, you simply create a class that extends unittest.TestCase. Below is an example.

“CellTest.py”

import unittest
from Cell import Cell

class CellTest(unittest.TestCase):
    def setUp(self):
        self.cell = Cell(AdminControl=AdminControl)

    def tearDown(self):
        pass

    def testGetName1(self):
        self.assertNotEqual(None, self.cell.getName())

    def testGetName2(self):
        id = AdminConfig.list('Cell')
        name = AdminConfig.showAttribute(id, 'name')
        self.assertEqual(name, self.cell.getName())

    def testGetName3(self):
        c = Cell()
        self.assertRaises(AttributeError, c.getName)

if __name__ == '__main__' or __name__ == 'main':
    #unittest.main() # not supported by wsadmin jython

    # alternative way of running this test
    suite = unittest.TestLoader().loadTestsFromTestCase(CellTest)
    unittest.TextTestRunner().run(suite)

    # other ways of running this test
    #suite = unittest.TestSuite()
    #suite.addTest(unittest.makeSuite(CellTest))
    #unittest.TextTestRunner().run(suite)

You will notice that I have commented out the line calling unittest.main(). This method call (as of WASv61) does not work with WebSphere’s wsadmin Jython. Fortunately, there are other ways to run a test case, as shown in the code above.

Also, you will notice that there is this odd __name__ == ‘main’ condition. In wsadmin Jython, this is what is being used and not ‘__main__’. For some reason, IBM chose not to use the standard.

Even though Websphere doesn’t support ‘__main__’, I still make it a habit of including it in my test cases for portability reasons. I wanted to have the flexibility of running my test cases using the standard CPython or Jython interpreter, if I need to, like using mock AdminConfig or AdminControl objects. This is also the reason why I chose to inject the AdminControl object into the Cell class instead of importing the module. [1] This easily allows the use of mock Admin* objects.

[1] Importing Websphere Admin* objects is not available by default. You have to set it up. See my other post on how to accomplish this – Websphere administrative objects not accessible from imported Jython modules.

Advertisements

Websphere’s Implementation of Jython (wsadmin) Broke __name__

Jython in IBM Websphere does not implement the global variable “__name__” the same way CPython or the official Jython does. Instead of setting it to “__main__” (if the module is not imported), it sets it to “main”.

For example, if you have this code to check if the script is being imported or not,

if __name__ == '__main__':
    run_method()

you have to write it differently with WebSphere Jython like this:

if __name__ == 'main':
    run_method()

It’s not a big deal, you’d say. Just check for both values if you are concerned about portability:

if __name__ == '__main__' or __name__ == 'main':
    run_method()

Unfortunately, that is not the only problem. There are libraries out there that use __main__. One example is unittest.main(). This method doesn’t work with Websphere Jython (WASv61). IBM ships this unittest module with Websphere v6.1, however, the unittest.main() method doesn’t work. Actually, the primary culprit is that Websphere’s Jython doesn’t implement the __main__ module.

As a workaround to this problem, don’t use unittest.main(). Instead, use the methods TestSuite() and TextTestRunner(). For example, instead of the usual way of writing your test class like this:

if __name__ == '__main__':
    unittest.main()

write it this way with Websphere Jython:

if __name__ == '__main__' or __name__ == 'main':
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(TestMyJythonClass))
    unittest.TextTestRunner().run(suite)

Websphere’s implementation of Jython in wsadmin broke sys.argv

In CPython and JPython, the first index of sys.argv points to the name of the Python script, while the rest of the array elements refer to the arguments. However, in Websphere’s Jython, the first index already points to the first argument of the script. This means that none of the elements of sys.argv has the information about the name of the script it is running.

I think this is a serious drawback. I have some functionalities where I need access to the name of the script. For example, I need to know the directory where that script is located. This will allow me to navigate relatively to other files where the script is located like additional path to libraries (pythonpath ) or configuration files and be able to access them at runtime.

I have yet to figure out a trivial way to access the script name at runtime in wsadmin. One workaround I am planning is to write a wrapper script to grab the script name and then pass that information to wsadmin’s Jython script as the first parameter, so that would become sys.argv[0]. Or perhaps as an option parameter with the value telling it that is the script name.

However, the problem with a wrapper script is that your wsadmin Jython scripts won’t be portable since it would be dependent on the wrapper to run.

If anyone out there has ideas other than a wrapper script, I’d love to hear it.