Sunday, October 27, 2019

Unit testing of circuit schematic operations - Part I

After setting up the test environments for the command line and the web app in the previous post, I now get started with some serious testing. To begin with, the code for this can be found in the testing branch in either of these two repositories:
https://sourceforge.net/p/pythonpowerelec/code/ci/testing/tree/
https://bitbucket.org/shivkiyer/ppe_simulator/branch/testing

I expanded the testing of csv_element_2D method to the testing of csv_element_2D and csv_tuple_2D as these two methods quite often go hand in hand since one converts a tuple to a string cell position and the vice versa. Here is the code:

There are two types of tests. First, the manual tests are tests where I know there could be a problem and these are the borderline cases - when for example 1Z becomes 1AA or 9A becomes 10A etc. Manual tests can be limited unless you have a lot of time or the function is very complicated and only manual tests are possible. In this case, automatic tests are also possible. For automatic tests, I randomly generate tuples of x (row) and y (column) positions and convert them into string cell positions using csv_element_2D and then convert the resultant cell positions back into tuples using csv_tuple_2D. I chose 20 such iterations and made asserts for each case.

The method can be extended to the Django web app with minor modifications - only the assert statement of pytest will be replaced by assertEqual of TestCase.

Next comes the csv_reader method that takes in a file object which is the .csv file and returns a 2D matrix (lists embedded within a list) representation of the circuit. This is the code:

It is important to write the desired circuit contents to a file. Simply passing the string contents to csv_reader as the argument will not work. This is because when Python opens a file, it creates an iterable object and so you can iterate line by line. However, if I pass a string, the iteration will happen character by character which is not what we want.

I didn't bother with circuit components or circuit topology because what matters here is the reading of the .csv file. A few errors are expected:
  1. If the separator is anything other than a comma, the reading fails.
  2. If one line has an extra character, the reading fails. This is because the expected result is a 2D matrix. This might be ok in most cases, but it might be a good idea to put a catch block in case, some weird spreadsheet software passes through a schematic with unequal line lengths. This should result in a user readable error rather than a syntax error.
  3. The elements are scrubbed internally which means leading and trailing spaces are removed from the elements. This is ok.

Will need to spend some thought on this as spreadsheet software in general are a bit messy and the need to generate .csv files in a particular form may not be obvious to the normal user.

Sunday, October 20, 2019

Starting unit testing in Python Power Electronics

My work as a web developer has introduced me to the wonders of unit testing. No one likes writing tests and neither did I for a very long time. I relied on manual testing like many others. Until I started writing unit tests in Jasmine for my web apps. The number of scenarios that you can test makes your code way more reliable.

Another advantage of testing is that quite often there are changes you would like to make but don't want to make them right now as you might break the code that is working. And in the end these changes never happen as you get too busy with other stuff. Here, test driven development can solve the problem of this inertia. Even if you do not want to change the code itself, test suites can help to totally determine every potential lapse in a block of code that will take away the fear bit by bit of finally getting around to fixing them.

With these noble thoughts, I have finally got over my laziness and decided to write tests for the circuit simulator. As a start, Python comes with the in-built unittest module for writing tests. Django uses this and creates a class TestCase to test API end points. Besides this, Python also has the pytest module that needs to be separately installed.

So, I decided to write unit tests for the command line interface with pytest and for the web app using Django's TestCase class based on unittest. In the beginning, the code for these will be separate. However, with time, the plan will be have one set of tests and also at the same time have one set of code for both web apps.

So, the directory structure for the simulator is:

README
command_line
            --  circuit_solver.py
            --  circuit_elements.py
            --  circuit_exceptions.py
            --  LICENSE.txt
            --  matrix.py
            --  network_reader.py
            --  solver.py

web_app
            -- requirements.txt
            -- simulator_interface
                     -- LICENSE.txt
                     -- manage.py
                     -- simulation_collection
                     -- media_files
                     -- simulator_interface
                                 -- settings.py
                                 -- urls.py
                                 -- wsgi.py
                     -- simulations
                                 -- circuit_solver.py
                                 -- circuit_elements.py
                                 -- circuit_exceptions.py
                                 -- solver.py
                                 -- network_reader.py
                                 -- matrix.py
                                 -- admin.py
                                 -- views.py
                                 -- models.py
                                 -- tests.py


There may be a few more directories and files, but these are the major ones at least for testing. We will create a tests directory inside command_line directory and house the command line app tests there. We will remove the tests.py file inside the simulations directory  inside web_app/simulator_interface and create a tests directory for the web app tests.

To install pytest, just do:
pip install pytest

For the command line app, I decided to get started with testing by writing a very simple test for the function csv_element_2D:



This function takes a tuple/list that represents a row, column position and converts it to a string that represents the cell position. So, [0,0] will be "1A" on a spreadsheet. So, a test for this function in the the command_line app would be:



The test itself if very simple. We import the method from the file network_reader.py that has the method. The test function has to start with test_ for pytest to execute it. The asset method in pytest merely asserts that for the test to pass, the value returned by the function for a particular argument has to be equal to a certain value.

The challenge in getting this test to run was in pytest being able to find the file/module network_reader.py. For this the first block of code above the function had to be written. I had to extract the parent method and insert it into the python path so that Python will look in that directory for the module and import it.

The test for the web app was a bit easier as I didn't have to struggle with path as much. The only challenge was I was trying to use pytest and it would not work as pytest for some reason could not find django module that is imported in the web app modules. The reason I can think of is in Django 2 onwards, the app needs to be loaded and therefore, django is not available until the app is working. For this, I had to go with the default Django TestCase class.



To run these tests. Inside command_line directory, just run the command:
pytest

There are several arguments that can be passed and I will investigate as time goes on. For the web app, inside the directory that contains manage.py, run:
python manage.py test simulations

The app name simulations is important because the tests directory with the test is inside the simulations app directory.