Components are best tested with TestNG and Mockito frameworks, as they already exist in the system design. TestNG is used to create more unit tests, check conditions, initialize tests, catch desired exceptions, etc. Mockito, on the other hand, is used to create implementations and inject objects that are necessary to test some functionality (e.g. when a component uses system ProcessService, UserService, etc.).
Example datachooser using UserFinder service:
@DataChooser public class FindingUserDataChooser { @Autowired private UserFinder userFinder; @Define public void definition( DataChooserDefinitionBuilder builder ) { // @formatter:off builder .id( "finding-user-datachooser" ) .name( "finding-user-datachooser-name" ) .description( "finding-user-datachooser-desc" ) .category( Categories.CUSTOM ) .mapping() .id( "fullname-mapping" ) .name( "fullname-mapping-name" ) .description( "fullname-mapping-desc" ) .create(); // @formatter:on } public void data( ComponentQueryData queryData, DataChooserResult result ) { String username = queryData.getQuery(); DetachedCriteria criteria = DetachedCriteria.forClass( User.class ); criteria.add( Restrictions.like( "userName", username ) ); List<User> users = userFinder.findByCriteria( criteria ); // itd... } }
The system UserFinder service will be injected into the component created in this way by the @Autowired annotation.
To use system services in plugins, you must also import them in the plugin descriptor (Import and share services).
An example test of the above class would be:
@Test public class FindingUserDataChooserTest { @InjectMocks FindingUserDataChooser component = new FindingUserDataChooser(); @Mock UserFinder userFinder; @Before private void init() { MockitoAnnotations.initMocks( this ); when( userFinder.findByCriteria( any( DetachedCriteria.class ) ) ).thenReturn( getSampleUserList() ); } private List<User> getSampleUserList() { User user1 = new User( "user1", "password" ); User user2 = new User( "user2", "password" ); return Lists.newArrayList( user1, user2 ); } @Test private void componentTest() { // ... } }
The UserFinder service has been dockerized by the @Mock annotation.
The static methods when and thenReturn determine the behavior of the UserFinder service for specific calls. Note that this is not a real system service, but only a temporary implementation for the duration of the tests. The @InjectMocks annotation says that to the created component object (FindingUserDataChooser), Mockito will try to inject implementations into its fields (in this case, it will inject UserFinder).
System services can also be injected into components by creating an annotated @Autowired constructor that has further services in its parameters. Example component:
@DataChooser public class FindingUserDataChooser { private UserFinder userFinder; @Autowired public FindingUserDataChooser( UserFinder userFinder ) { this.userFinder = userFinder; } @Define public void definition( DataChooserDefinitionBuilder builder ) { // @formatter:off builder .id( "finding-user-datachooser" ) .name( "finding-user-datachooser-name" ) .description( "finding-user-datachooser-desc" ) .category( Categories.CUSTOM ) .mapping() .id( "fullname-mapping" ) .name( "fullname-mapping-name" ) .description( "fullname-mapping-desc" ) .create(); // @formatter:on } public void data( ComponentQueryData queryData, DataChooserResult result ) { String username = queryData.getQuery(); DetachedCriteria criteria = DetachedCriteria.forClass( User.class ); criteria.add( Restrictions.like( "userName", username ) ); List<User> users = userFinder.findByCriteria( criteria ); // itd. } }
In this case, the UserFinder service will also be correctly pulled by the component. You can test the component in the following way:
@Test public class FindingUserDataChooserTest { FindingUserDataChooser component; @Mock UserFinder userFinder; @Before private void init() { MockitoAnnotations.initMocks( this ); when( userFinder.findByCriteria( any( DetachedCriteria.class ) ) ).thenReturn( getSampleUserList() ); component = new FindingUserDataChooser( userFinder ); } private List<User> getSampleUserList() { User user1 = new User( "user1", "password" ); User user2 = new User( "user2", "password" ); return Lists.newArrayList( user1, user2 ); } @Test private void componentTest() { // ... } }
Remember also that component type-specific objects must be created for component calls. For example, for the above DataChooser, you can see that the data method accepts ComponentQueryData and DataChooserResult objects, which you need to create or mothball yourself in testing.