How to efficient make unittests?

Hi!

Anvil is an absolute cool project!

I am trying to build this app, which could be useful in some biochemistry lab.

Problem: A lab full of machines, running different sofware with different output.
Solution: Drag and drop feature in the laveb, which extracts data from files and export to database
In the app, get an overview of data submitted and table to acquire from.

For example a machine with:

  • Akta purifier: Have .res files. Load the file and inspect
  • Nanotemper: Have .xlsx files. Load the file, and
  • Jasco-810: .jws files.

There will come a problem with this, that I would need to efficient unittest
inputs. So, while I am working on the different formats, the other format handlers are not screwed.

What would be the best strategy for this?

1 Like

It might be easier to understand with a prototype:

https://NW7T4CHXKJICCX4R.anvilapp.net/IGTVGPVDUWVTDECZ2LQLSMJ5

Hi there, and welcome to the forum! :slight_smile:

First things first - I tried to click on the link to your prototype, but it said “App not found”. Did you delete the app (or reset the sharing key)?

For unit testing, we support the standard unittest module. (If you prefer another testing module, we can install it by request for Pro accounts.) I’d suggest keeping your reference files as app files in Google Drive. If you’re using mechanisms that require loading data from a file (eg pandas for .xlsx files), you can try writing the data to a temporary file in /tmp in the “Full Python” runtimes. (Use get_bytes() to get the content of a Media object as a binary string).

Hope that helps!

1 Like

Hi meredydd.

Thank you for returning to my question.

The link is:
https://NW7T4CHXKJICCX4R.anvilapp.net/BHNX6A6AWJRCGNMCXEAIJY23

To unlock upload, use the passwd “test”.
To change database information: unlock use the passwd “test”.
Database connection is though not established yet. Only an upload log functionality
and a plot for the counts of upload.

Please try it out. :slight_smile:

What I need now, is exactly two things.

  • Load different file formats, and understand input. Use of: pandas
  • Extract data, export to database. I have made a trial account at: https://www.elephantsql.com/
    Use of: psycopg2

In either case, I would need the “PRO” version.
An I am tempted to try.

But, to effectively progress in filetype development, I really need to first
established unittest methods.

Is there any tutorial for a minimum showcase how this could be done?
How do I launch the code with unittest?

I got a simple solution to run.

The unittest module is limited, and there is no logging functionality.
So the output of the test can only be seen for developer, in the Output sidebar.

I made a file “test_me” in the MODULES section. It contains this:

import unittest

class TestMethods(unittest.TestCase):
    def test_string_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_string_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_string_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])

    def test_math_multiplikation(self):
        self.assertEqual(2*2, 5)

# What is available in unittest this sandbox? 
#print(dir(unittest))
# ['TestCase', '__author__', '__doc__', '__file__', '__name__', '__path__', 'main']

# What is available in unittest.main this sandbox? 
# https://docs.python.org/2/library/unittest.html
# unittest.main([module[, defaultTest[, argv[, testRunner[, testLoader[, exit[, verbosity[, failfast[, catchbreak[, buffer]]]]]]]]]])

# 0 (quiet): you just get the total numbers of tests executed and the global result
# 1 (default): you get the same plus a dot for every successful test or a F for every failure
# 2 (verbose): you get the help string of every test and the result
unittest.main(verbosity=1)

And then I made FORM page, with a single button “Perform unittest”.

from anvil import *
import anvil.server
import tables
from tables import app_tables
import anvil.users

class Page_unittest (Page_unittestTemplate):
  def __init__(self, **properties):
    # You must call self.init_components() before doing anything else in this function
    self.init_components(**properties)

    # Any code you write here will run when the form opens.

  def button_unittest_click (self, **event_args):
    # This method is called when the button is clicked
    import test_me
    Notification("Unittests complete",title="Unittesting:", style="info").show()

Great to see! Yes, that’s exactly how we’d recommend running unit tests. One suggested tweak - I would make a function in the test_me module that runs unittest.main(), rather than running it automatically when you import the module. That’s just a convention thing - it’s normal for Python modules not to do anything in particular when you import them.

Also, here’s a fun trick - here’s a way of showing a Notification while the unit test is running:

with Notification("Unit tests running..."):
  import test_me
  test_me.run_tests()

That way, the Notification will show while that code is running, and then disappear when it completes :slight_smile:

3 Likes

@meredydd : I like that notification trick. I take it I could use that for any process (for example a server call) that is likely to run for a noticeable period of time?

@david.wylie Absolutely!

1 Like

I’ve tried to do this on the server side using a server module which I call from a form:

import unittest

import anvil.server


@anvil.server.callable
def run_tests():
  unittest.main()
  
  
class TestMethods(unittest.TestCase):
    def test_string_upper(self):
        self.assertEqual('foo'.upper(), 'BAR')

but I get:

SystemExit: False
at /usr/local/lib/python3.6/unittest/main.py, line 258
  called from /usr/local/lib/python3.6/unittest/main.py, line 95

Am I missing something obvious?

By default, unittest.main() exits when it’s done (which means that run_tests() doesn’t return normally, which Anvil interprets as an error!).

You probably want:

unittest.main(exit=False)

oh good grief. that obvious?!!!

Thanks @meredydd

That now runs ok (once I also specified the module in the unittest.main() call).

Next dumb question: How do I get the output to appear?

The docs seem to suggest it should work:

main supports being used from the interactive interpreter by passing in the argument exit=False. This displays the result on standard output without calling sys.exit()

but I’m not seeing anything.

I added a simple print statement into the test method so I can be certain it’s being run and that shows up in the output just fine.

Is this a buffering problem now that the exit=False argument is used, I wonder?

Got it!

My run_tests function needs to be:

@anvil.server.callable
def run_tests():
  test = unittest.TestLoader().loadTestsFromTestCase(TestMethods)
  unittest.TextTestRunner(stream=sys.stdout).run(test)

and all works fine

1 Like

A slight improvement to run_tests:

import sys
import unittest
import anvil.server

test_modules = ['test_module_1', 'test_module_2']

@anvil.server.callable
def run_tests(verbosity=0):
  test = unittest.TestLoader().loadTestsFromNames(test_modules)
  unittest.TextTestRunner(stream=sys.stdout, verbosity=verbosity).run(test)

The default verbosity level is minimal output. Increase the number to see more.

4 Likes