Difference between revisions of "Unit Testing"
Line 91: | Line 91: | ||
## The change in GL account balances are correct: inventory value increases and are offset by raw materials and manufacturing expenses. | ## The change in GL account balances are correct: inventory value increases and are offset by raw materials and manufacturing expenses. | ||
## The financial statements are in balance at all times. | ## The financial statements are in balance at all times. | ||
− | ## The financial transactions created by this production run is in agreement with the reference transactions MFGTEST-1, -2, -3. | + | ## The financial transactions created by this production run is in agreement with the reference transactions MFGTEST-1, -2, -3. This is done by finding all new financial transactions after the production run has been begun, as they should only be generated by the production run. |
## The unit value of the finished product is correct. | ## The unit value of the finished product is correct. | ||
Revision as of 18:57, 29 October 2007
Contents
How to Write Unit Tests
opentaps 1.0
For opentaps 1.0, you would write a set of Junit tests in a class, then define it in an XML testdef file like this:
<test-suite suite-name="entitytests" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/test-suite.xsd"> <test-case case-name="security-tests"><junit-test-suite class-name="com.opensourcestrategies.crmsfa.test.SecurityTests"/></test-case> </test-suite>
You can define multiple tests per testdef xml file. Then, add the testdef file to your ofbiz-component.xml, like this:
<test-suite loader="main" location="testdef/crmsfa_tests.xml"/>
Then, when you do
$ ant run-tests
your tests will be run.
opentaps 0.9
In opentaps 0.9, you would write your Junit tests class and add your it to the base/config/test-containers.xml file, in the "junit-container" at the bottom, like this:
<container name="junit-container" class="org.ofbiz.base.container.JunitContainer"> <property name="base-test" value="org.ofbiz.base.test.BaseUnitTests"/> <property name="entity-test" value="org.ofbiz.entity.test.EntityTestSuite"/> <property name="service-test" value="org.ofbiz.service.test.ServiceEngineTests"/> <property name="crm-security" value="com.opensourcestrategies.crmsfa.test.SecurityTests"/> <!-- your unit tests --> <!-- <property name="usps-test" value="org.ofbiz.shipment.thirdparty.usps.UspsServicesTests"/> <property name="jxunit-test" value="net.sourceforge.jxunit.JXTestCase"/> --> </container>
Then you would do
$ ant run-tests
Your tests will run alongside the existing OFBIZ test suites.
IMPORTANT: Use a "test" delegator to point your tests to a separate database, and make sure it is defined in framework/entity/config/entityengine.xml is set to the right database.
Setting Up For Unit Testing
We recommend that you create a separate database on the same database server for testing purposes and install all demo data into the testing database. Let's say that this database is called "opentaps_testing". Then, edit the file framework/entity/config/entityengine.xml and define opentaps_testing as a new datasource, called "localmysltesting" or "localpostgrestesting". Next, initiate the demo data into the testing database by editing the default delegator:
<delegator name="default" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main" distributed-cache-clear-enabled="false"> <group-map group-name="org.ofbiz" datasource-name="localXXXtesting"/> </delegator>
Then do an
$ ant run-install
to install all the seed and demo data into the testing database. Then you can edit the default delegator back to your original delegator, and set the test delegator to the testing database:
<delegator name="test" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main"> <group-map group-name="org.ofbiz" datasource-name="localXXXtesting"/> </delegator>
All unit tests should be run to use the test delegator. This can be done by instantiating the the "test" delegator by name and using that delegator to instantiate a dispatcher. Or you can just write a test suite which extends the OpentapsTests base class, which does it for you.
If you need to modify port settings for the testing instance, you should edit the file framework/base/config/test-containers.xml.
Unit Testing Strategies
These are some strategies for unit testing:
- Transaction comparison - Compare the transaction produced with a sample transaction, possibly pre-loaded into the system. For example, posting a paycheck to the ledger and then comparing with test data of a correct ledger transaction to make sure that they are equivalent.
- State change - Compare the state of the system before and after a transaction has occurred. For example, check the inventry of an item, then ship an order, and check the resulting inventory to make sure that it is correctly decremented. This could get very complex: Shipping an order could cause customer balances, invoices, ledger postings, and inventory changes. Multiple tests could be run off the same transaction event.
- Absolute state check - At all times, certain relationships must hold. For example, the sum of all debits must equal sum of all credits.
Tests should be written against the services that create the original data. For example, if you are writing tests against CRMSFA activity, you can use users from the demo data set, but you should use the CRMSFA activity services to create or update your activities. Otherwise, if you create those activities with some other method, future changes to the services to create activities will not be covered by your unit tests.
Tests should be run against a dedicated testing database with demo and seed data rather than production data. Therefore, the tests generally should set up their own initial conditions and run to completion, but they do not need to "tear down" and remove all data created by the tests. (This would be very impractical: imagine creating and shipping an order. To tear it down would involve reverting order data, customer information, inventory data, shipment data, invoices and payments, and accounting entries.) A good test for the tests is that if you ran the test suite in succession multiple times, they should pass during the second and third runs as well as the first run.
A Unit Testing Tutorial
Now let's walk through a particular unit test and see how it works. The one that we're looking at is the ProductionRunTests.java's testProductionRunTransactions method. This particular test verifies that a standard manufacturing process is working correctly and checks the inventory results and financial statements. As you read through the code, you will notice that it does the following
- Sets up by first receiving the raw materials (MAT_A_COST and MAT_B_COST) into inventory
- Checks the initial state by getting the GL account balances and the initial inventory quantities, both ATP and QOH
- Runs through the production run
- Checks the final state by getting the GL account balances and the inventory quantities for raw materials and the finished product.
- Verify the following:
- The change in inventory quantities is correct: raw materials are used, so their quantities are reduced, and finished product's quantity is increased because it is produced.
- The change in GL account balances are correct: inventory value increases and are offset by raw materials and manufacturing expenses.
- The financial statements are in balance at all times.
- The financial transactions created by this production run is in agreement with the reference transactions MFGTEST-1, -2, -3. This is done by finding all new financial transactions after the production run has been begun, as they should only be generated by the production run.
- The unit value of the finished product is correct.
Along the way, the tests will verify that all the services are run correctly and return success as well.
The test case uses the classes InventoryAsserts and FinancialAsserts to obtain information and run tests on the inventory balances and financial statement values. This is a common "delegation" pattern to separate the code for testing assertions to new classes. It also uses methods such as assertMapDifferenceCorrect and assertTransactionEquivalence which are inherited from the OpentapsTestCase and FinancialsTestCase base classes.
Debugging Unit Tests with IntelliJ
The default task for tests will do a global compile. To skip this, you can redefine the run-tests target in build.xml as follows,
<target name="run-tests"> <java jar="ofbiz.jar" fork="true"> <arg value="test"/> </java> </target>
Using a debugger can help speed up development of the unit tests. You can enable debugging by specifying the JVM arguments for your debugging system. For instance, if you have the IntelliJ IDE, the run-tests target becomes,
<target name="run-tests"> <java jar="ofbiz.jar" fork="true"> <jvmarg value="${memory.max.param}"/> <jvmarg value="-Xdebug"/> <jvmarg value="-Xnoagent"/> <jvmarg value="-Djava.compiler=NONE "/> <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"/> <arg value="test"/> </java> </target>
You should be able to attach the debugger immediately after running ant run-tests. Don't forget to recompile the component where your tests live.
Another tip is to comment out all unnecessary test suites. Unfortunately, this involves searching every ofbiz-component.xml. One way to find them, if you're on a POSIX OS, is to use find,
$ find . -name ofbiz-component.xml -exec grep test-suite {} \; -print