2009 Implementers Group Meeting Program Development Best Practices
Development Best Practices
Venue: Breakaway 1
Unit testing - automated framework to test applications.
A unit test is one small unit of work. Functional tests to test a range of actions.
Define tests w/ specific names - easy to write and debug
Junit4 - add @Test annotation to the class
Behaviour-driven tests - testing one use case "shouldGetAllDeclairdedFueldsGivenChoice"
OpenMRS wrote class to link actual method to test
look at javadoc in example class - see @should annotation - @should get all declared fields on given class
Eclipse plugin to generate tests from the @should's - creates stub test class
Right-click Run as Unit test, displays unit test view.
String comparison in right panel
In example, the test already identified a potential NPE
To run a whole package, right-click on the package in Project explorer and select Run as junit test.
In openmrs, we do not check in failing unit tests
2 use cases when tests
New user - help her check if her changes are ok.
Merging branches from other people
Takes 2 mins to run through entire suite.
Ant task - provides html output from tests.
Continuous integration server.
Writing a new unit test (example)
All of the implementations should have the same "@shoulds" as their interfaces, so the @shoulds should be on the interface.
In-memory database in the test framework.
contains demo data: 1-2 rows per table
Dataset found in test framework.
db defined w/ xml and uses dbunit
In example, line 186 - form definition - form 1
In unit test:
@Test
@Verified
public void getForm_shouldReturnGivenFormNotNull() {
Assert.assertNotNull("Form should not be null", Context.getFormService.getForm(1));
}
To add more fields to test, you may add another xml file w/ the fields
Copy and paste existing xml file and change the rows
Adding a new method:
Best practice:
Add @should's as you are writing code.
Example javadoc:
@param argument1
@param argument2
@should not fail given name
@should get forms that start with names
@should not get forms that only contain names
public List getFormsByName(String name);
Open FormServiceImpl, use overwrite/implement method
Return to FormService
Use plugin to generate tests from @shoulds
Best practice:
If your unit test is not doing and assert, it is not doing any good...
Don't use System.out.println...
Only asserts and exceptions deliver reds and greens (aka failures and successes) in junit.
The more specific the assert the better.
Magic :
Shows field answers and form table contents
TestUtil.printOutTableContents(getConnection(), "field_answer")
5 sec startup time to create database and the FormService object via Spring
Adding to Initial Data Set
To add something to field answer table and dump to an xml file:
This uses the DB defined in runtime.properties
In TestUtil.createIntitialDataset class
modify this class.- change the initialDataset.addTable.to point to the table
Run like a normal unit test
creates a new output.xml file describing the rows
need to remove @ignore from CreateInitialDataset - a junit annotation
To run this dataset before I run the unit test.
This test extends BaseContextSensitiveTest, which provides useful method - executeDataset
takes package hierarchy of the file.
uses INITIAL_FIELDS_XML
Best practice:
Shouldn't execute test against your db, use the in-memory db (which is set up with the xml files)
If you run against your own mysql db, someone else may not be able to use your test if they don't have mysql and your specific dataset
Error message - the in-memory db does not know column - define in hibernate mapping.
Every time test runs, it will roll back changes to the database. Resets db.
@Verifies - created by eclipse plugin to figure out which unit test has already been created
org.openmrs.test
Tests named same as interface w/ "Test" appended.
Not required by Junit.