Author: jawi
Date: Wed Aug 15 11:02:13 2012
New Revision: 1373330

URL: http://svn.apache.org/viewvc?rev=1373330&view=rev
Log:
Added new tutorial on writing tests.

Added:
    ace/site/trunk/content/dev-doc/writing-tests.mdtext   (with props)

Added: ace/site/trunk/content/dev-doc/writing-tests.mdtext
URL: 
http://svn.apache.org/viewvc/ace/site/trunk/content/dev-doc/writing-tests.mdtext?rev=1373330&view=auto
==============================================================================
--- ace/site/trunk/content/dev-doc/writing-tests.mdtext (added)
+++ ace/site/trunk/content/dev-doc/writing-tests.mdtext Wed Aug 15 11:02:13 2012
@@ -0,0 +1,143 @@
+Title: Writing unit/integration tests
+
+This tutorial describes how to write unit and integration tests for ACE. For 
its unit tests, ACE relies on [TestNG](http://testng.org), while for the 
integration tests a combination of [BndTools](http://www.bndtools.org) and 
[JUnit](http://junit.org) is used.
+
+
+## Writing unit tests
+
+Unit tests test the behavior of classes or methods in complete isolation. In 
case this is not possible, due to dependencies on other classes, one can make 
use of a mocking framework such as [Mockito](http://code.google.com/p/mockito/) 
to replace those dependencies with stub implementations.
+
+Writing unit tests with TestNG is much like writing tests with JUnit: you 
create a class which contains methods annotated with the `@Test` annotation. 
Also, you can add methods that are run before or after each test or test case. 
One feature that is distinct to TestNG is the ability to group tests, making it 
possible to run only a certain group of tests instead of all[^1].
+
+All unit tests are placed in the same project they are testing, in a separate 
directory named `test`. This allows the tests to be compiled and run 
independently from the remaining code. It is good practice to have the same 
package structure for your tests and other code. 
+
+### Example
+
+Lets take a look at an excerpt from the unit test[^2] for 
`AuthenticationServiceImpl`:
+
+    :::java
+    public class AuthenticationServiceImplTest {
+       private LogService m_log;
+        
+       @BeforeMethod(alwaysRun = true)
+       public void setUp() {
+           m_log = Mockito.mock(LogService.class);
+       }
+
+       /**
+        * Tests that an exception is thrown if a null context is given.
+        */
+       @Test(groups = { "UNIT" }, expectedExceptions = 
IllegalArgumentException.class)
+       public void testAuthenticateFailsWithNullContext() {
+          new AuthenticationServiceImpl(m_log).authenticate((Object[]) null);
+       }
+
+       /**
+        * Tests that without any authentication processors, no authentication 
will take place.
+        */
+       @Test(groups = { "UNIT" })
+       public void testAuthenticateFailsWithoutAuthProcessors() {
+           Assert.assertNull(createAuthenticationService().authenticate("foo", 
"bar"), "Expected authentication to fail!");
+       }
+       
+       // ...
+    }
+
+This snippet shows us almost all important concepts for TestNG:
+
+* The `@BeforeMethod` annotation allows us to run a method before each 
individual test. In this 'setUp' method, we create a stub implementation of a 
<tt>LogService</tt>. **Note:** the `alwaysRun = true` is needed to ensure that 
this method is run, even though it does not belong to any test-group;
+* The method `testAuthenticateFailsWithNullContext` is annotated with the 
`@Test` annotation, and its parameters tell us two more things: it belongs to a 
group UNIT, and there's a failure to expect in the form of an 
'IllegalArgumentException'. In this method, we instantiate the class-under-test 
(using the stub 'LogService') and invoke a method on it;
+* The last method (`testAuthenticateFailsWithoutAuthProcessors`) shows us how 
to make assertions on the results of methods. The `Assert` class of TestNG is 
almost equivalent to its equally named counterpart in JUnit with one 
difference: the failure message always comes last.
+
+To run the unit tests for a project, you simply go to the root directory of 
the project itself, and call:
+
+    :::sh
+    $ ant testng
+
+This will run the unit tests using TestNG. The output of the tests can be 
found in the `test-output` directory of your project. To run the test from 
Eclipse, you can right click on it, and select `Run As -> TestNG Test` from its 
context menu.
+
+
+## Writing integration tests
+
+In an integration test you test whether a small part of your system works as 
expected. For this, you need to set up an environment which resembles the 
situation in which the system is finally deployed. For ACE, this means that we 
need to set up an OSGi environment and a subset of the bundles we want to test. 
Fortunately, BndTools provides us with easy tooling to just do that with the 
use of [JUnit3](http://pub.admc.com/howtos/junit3x/)[^3].
+
+### Integration test principles
+
+To write a very basic (OSGi) integration test, you need to extend your test 
from `junit.framework.TestCase`. To access the bundle context of your test 
case, you can make use of some standard OSGi utility code:
+
+    :::java
+    public class MyIntegrationTest extends junit.framework.TestCase {
+        private volatile org.osgi.framework.BundleContext m_context;
+        
+        //...
+        
+        protected void setUp() throws Exception {
+            m_context = 
org.osgi.framework.FrameworkUtil.getBundle(getClass()).getBundleContext();
+        }
+    }
+
+With this in place, we can now make OSGi calls from our test methods.
+
+To ease the writing of integration tests, ACE provides a 
`IntegrationTestBase`[^4] helper class that provides you with some  boilerplate 
code commonly used in integration tests. Internally, it makes use of Felix 
Dependency Manager to control and manage the desired service dependencies. 
+
+### Example
+
+In this section, we'll describe how to write an integration test that makes 
use of a 'UserAdmin' service in its test method.
+
+The first we need to do is create our test class:
+
+    :::java
+    public class UserAdminIntegrationTest extends IntegrationTestBase {
+        private volatile org.osgi.service.useradmin.UserAdmin m_userAdmin;
+        
+        protected org.apache.felix.dm.Component[] getDependencies() {
+            return new org.apache.felix.dm.Component[] {
+                createComponent()
+                    .setImplementation(this)
+                    
.add(createServiceDependency().setService(org.osgi.service.useradmin.UserAdmin.class).setRequired(true))
+            };
+        }
+        
+        // ...
+    }
+
+Having explained that our test depends upon the 'UserAdmin' service, and that 
its a required dependency, means that our test won't run if this service is not 
available[^5].
+
+Sometimes, you need to do some additional set up before or after the 
dependencies are resolved. For example, one might need to provision some 
configuration in order to get dependencies in the correct state, or wait until 
some other condition is met after all dependencies have been resolved. In order 
to do this, one can override the `before()` or `after()` methods in 
`IntegrationTestBase`. For our simple integration test, however, we do not need 
such "advanced" set up.
+
+The only thing left is the actual test itself. As the 'UserAdmin' service is 
being tracked for us, we can simply direct use it in our test:
+
+    :::java
+    public class UserAdminIntegrationTest extends IntegrationTestBase {
+        // ...
+        
+        public void testUserAdminCreateGroupRoleOk() throws Exception {
+            org.osgi.service.useradmin.Role group = 
+                m_userAdmin.createRole("MyGroup", 
org.osgi.service.useradmin.Role.GROUP);
+            
+            Assert.assertNotNull(group);
+            Assert.assertEquals("MyGroup", group.getName());
+        }
+        
+        // ...
+    }
+
+That is it! The only thing left is to run the test, which is as simple as:
+
+    :::sh
+    $ ant test
+
+When running Eclipse, you can also run your integration test by right clicking 
on it, and selecting `Run As -> OSGi JUnit Test` from its context menu.
+
+
+## Notes
+
+[^1]: The consequence is that TestNG also allows you to write other kinds of 
tests than unit tests, like integration tests or performance tests. ACE does 
not make use of this kind of functionality, and only uses TestNG for its unit 
testing.
+
+[^2]: This test can be found in the `org.apache.ace.authentication` project.
+
+[^3]: The current version of BndTools still uses JUnit3, while there is some 
effort already to support JUnit4 as well, see: 
https://github.com/bndtools/bnd/issues/156.
+
+[^4]: This helper class can be found in the `org.apache.ace.test` project.
+
+[^5]: To be more precise: the test will wait a couple of seconds to allow all 
dependencies to be satisfied. If after this waiting period any dependency is 
not satisfied, the test will fail.

Propchange: ace/site/trunk/content/dev-doc/writing-tests.mdtext
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to