Skip to end of metadata
Go to start of metadata

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.

  • No labels