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.