Help designing unit tests?

How do people write unit tests with Ignition? I have finally, FINALLY, refactored this project and completely decoupled the business logic from the GUI and so the first thing I want to do is write some unit tests so anytime we make a change, we can check all the usual processes and make sure nothing unexpectedly broke, move away from the "production is the QA environment" paradigm we have going on now.

This app is 99% CRUD routes for various db forms. So right now I am writing functions that are testing the creation/update/deleting of records and checking I get the appropriate result.

Here's my first attempt -

def customerTests():
	# Create a new customer, customer name TestCustomer1000
	newCustomer = {...}
	newCreateResult = forms2.Forms.Customer.create(newCustomer)
	print 'creating customer'
	assert newCreateResult.success == True
	print 'passed'
	# Update the new customer to TestCustomer1001
	updateCustomerData = {...}
	# newCreateResult.result is the idx of the newly created one
	customerUpdateResult = forms2.Forms.Customer.update(updateCustomerData, newCustomer, newCreateResult.result)
	print 'updating..'
	assert customerUpdateResult.success == True
	print 'passed'
	# Try to update customer name to existing Customer Name- this should fail
	updateToExistingCustomerName = {...}
	shouldFailUpdate = forms2.Forms.Customer.update(updateToMesser, updateCustomerData, newCreateResult.result)
	print 'trying fail update'
	assert shouldFailUpdate.success == False
	print 'passed'
	# Delete the new customer
	newDeleteResult = forms2.Forms.Customer.delete(newCreateResult.result)
	print 'deleting..'
	assert newDeleteResult.success == True
	print 'passed'

And then I would call this and just make sure no assertion error comes up. I am wondering if this is how most people do it or if there is other better methods for writing unit tests specifically within the context of Ignition.

While this won't make you design better tests, it can be helpful to write them.

click for code
import ast

"""
Basic test helper

Import test in your file: from utils.test import test

Decorate your functions to be tested:
@test
def test_foo():
    foo = "foo"
    bar = get_bar()
    assert foo == bar, "expected {}, got {}".format(foo, bar)

Call the function individually, or use run_tests() to run all the marked functions in modules:
run_tests(module_1, module_b)
"""

class TestFinder(ast.NodeVisitor):
	def visit_Module(self, node):
		self.test_funcs = []
		self.generic_visit(node)

	def visit_FunctionDef(self, node):
		if "test" in (deco.id for deco in node.decorator_list):
			self.test_funcs.append(node.name)
	
	def get_testfuncs(self):
		return self.test_funcs

def test(func):
	"""
	Decorator to mark functions to be tested.
	
	Simply import the decorator (from Tests.utils import test) and add @test before the function
	"""

	def wrapper():
		try:
			func()
		except AssertionError as e:
			message = "failed ({})".format(e)
		else:
			message = "passed"
		return message
	return wrapper


def run_tests(*modules):
	"""
	Run all the functions marked as tests (with the test decorator) in every packages passed as parameter.
	"""
	for m in modules:
		print("{}\ntesting functions in {}...".format("-"*20, m.name))

		tree = ast.parse(m.code)
		f = TestFinder()
		f.visit(tree)
		funcs = f.get_testfuncs()

		if not funcs:
			print("No functions marked for testing found in {}".format(m.name))
		for f in funcs:
			try:
				message = getattr(m, f)()
				print("{:.<40}{}".format(f, message))
			except BaseException as e:
				print(repr(e))

image

Write your tests as simple functions, decorate them with @test, then run run_test(module1, module2, etc) wherever you want (usually the script console though).
I haven't used it in a while, and if I remember correctly there were a few quirks. I may or may not have fixed them.

edit: forgot to format code :X

note: You can use run_tests() to run all the tests in the modules passed as arguments, or just call a function that's decorated with @test directly

3 Likes

Something went wrong with your code formatting there.

What went wrong with the formatting was that there was no formatting :smiley:

2 Likes