Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Requirements

jbdevstudioqajenkins edited this page Jul 1, 2015 · 79 revisions

A requirement represents one prerequisite required by a test (e.g. created user or running server). Each requirement consists of two parts:

  • requirement declaration (in a form of annotation)
  • code for fulfilling the requirement (java class)

The biggest difference between a Red Deer requirement and the the @BeforeClass annotation from the JUnit framework is that if a requirement cannot be fulfilled the test is not executed. This is implemented via RedDeerSuite class therefore all tests requiring requirement functionality has to be run with RedDeerSuite suite i. e. has to be annotated with annotation @RunWith(RedDeerSuite.class)

There are (4) ways to define requirements:

Complete source code of used code snippets could be found here RedDeer Examples. To use it just clone RedDeer repo and import org.jboss.reddeer.examples plugin to Eclipse workspace.


Simple Requirement 

Simple Requirement

A simple requirement consists of just an annotation without any parameters and the fulfilling class.

Example

Let's say you have a test that requires a database admin user account to be defined. You do not care if the user account already exists when the test is run. If the account does not exist, you want it to be created before the test methods are executed.

Requirement usage

This is how you declare the requirement that admin user has to be present in the database:

package org.jboss.reddeer.snippet.test;

import org.jboss.reddeer.junit.runner.RedDeerSuite;
import org.jboss.reddeer.snippet.requirement.AdminUserRequirement.AdminUser;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(RedDeerSuite.class)
@AdminUser
public class AdminUserTest {
       
	@Test
    public void test(){
		// test admin user
	}
}

source code

Requirement Fulfilling Class

This is how you create a class for fulfilling the declared requirement (the annotation needs to be an inner member of the class implementing Requirement interface)

package org.jboss.reddeer.snippet.requirement;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.jboss.reddeer.junit.requirement.Requirement;

public class AdminUserRequirement implements Requirement {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface AdminUser {
    }

	public void setDeclaration(Annotation declaration) {
		// no need to access the annotation
	}

	public boolean canFulfill() {
		 // return true if it is possible to connect to database, false otherwise
        return true;
	}

	public void fulfill() {
		// create an admin user in the database if admin user does not exists
		
	}

	public void cleanUp() {
		// perform clean up
	}
}

source code


Requirement with Parameters 

Requirement with Parameters

A requirement with parameters consists of an annotation with one or more several parameters and a fulfilling class that can access those parameters.

Example

If you do not want a special requirement for each database user you can create an annotation with a user name parameter. The requirement fulfilling class code will have access to that parameter and will create appropriate users in the database if needed.

Requirement usage

This is how you declare the requirement that a user with name ''admin'' has to be present in the database:

package org.jboss.reddeer.snippet.test;

import org.jboss.reddeer.junit.runner.RedDeerSuite;
import org.jboss.reddeer.snippet.requirement.UserRequirement.User;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(RedDeerSuite.class)
@User(name = "admin")
public class UserTest {
	
	@Test
	public void test() {
	}
}

source code

Requirement Fulfilling Class

This is how you access the parameters of the declared annotation.

package org.jboss.reddeer.snippet.requirement;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.jboss.reddeer.junit.requirement.Requirement;

public class UserRequirement implements Requirement<UserRequirement.User> {
	
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface User {
		String name();
	}

	private User user;

	public boolean canFulfill() {
		// return true if you can connect to the database
		return true;
	}

	public void fulfill() {
		System.out.println("Fulfilling reuirement User with name: "
				+ user.name());
		// create an user in the database if user does not exist
	}

	public void cleanUp() {
		// perform clean up
	}

	public void setDeclaration(User user) {
		this.user = user;
	}
}

source code


Requirements Configuration

The Red Deer JUnit component provides support for easy test run configuration by providing its own XML schema.

To create a test run configuration you only need to create the XML file and point Red Deer either to that file or to the folder containing several XML configuration files by specifying the following property on the command line when you run your test:

-Dreddeer.config=/home/path/to/file/or/directory

You can make use of this configuration file to configure your own requirements in two ways:

  • Use a simple property based (key-value) configuration. To do this, you will need to provide setter methods in your requirement fulfilling class for each configuration property, or
  • Create your own XML schema that can be hooked into the XML configuration file. To do this, you will need to create a special configuration object that will be injected into your requirement.

The following sections describe each of these two approaches.


Requirements with Property Based Configuration 

Approach 1 - Requirements with Property Based Configuration

This approach makes use of a simple, property based, configuration in a Red Deer configuration file. This approach requires a mandatory XSD schema. In the future, this schema will be available on public site. Until then you need to have it locally available in the configuration validation phase (you can download it here: https://github.com/jboss-reddeer/reddeer/blob/master/plugins/org.jboss.reddeer.junit/resources/RedDeerRequirements.xsd).

Example

You probably do not want to hard-code some database information into your Java code. Instead, you can put that information into a Red Deer configuration file:

Configuration file

<?xml version="1.0" encoding="UTF-8"?>
<testrun 
	xmlns="http://www.jboss.org/NS/Req" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:server="http://www.jboss.org/NS/ServerReq"
	xsi:schemaLocation="http://www.jboss.org/NS/Req http://www.jboss.org/schema/reddeer/RedDeerSchema.xsd">

	<requirements>
		<requirement class="org.jboss.reddeer.snippet.requirement.simple.UserRequirement"
			name="userRequirement">
			<property key="name" value="USERS_ADMINISTRATION" />
			<property key="ip" value="127.0.0.1" />
		</requirement>
	</requirements>
</testrun>

source code

Requirement usage

This is how you declare the requirement that user with name ''admin'' has to be present in the database:

@RunWith(RedDeerSuite.class)
@User(name="admin")
public class UserTest {
        @Test
        public void test(){

        }
}

Requirement Fulfilling Code

Since it is possible to use requirements without any configuration, you need to let Red Deer know that you'd like to use property based config by implementing the PropertyConfiguration interface.

public class UserRequirement implements Requirement<User>, PropertyConfiguration{

	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface User {
	}
	private String name;

	private String ip;

	public boolean canFulfill() {
		// return true if you can connect to the database
		return true;
	}

	public void fulfill() {
		// create an admin user in the database if it does not exist yet
	}

	public void setDeclaration(User user) {
		// annotation has no parameters so no need to store it
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}
}

Requirements with a Custom Schema 

Approach 2 - Requirements with a Custom Schema

This approach makes use of a Red Deer configuration file but also provides its own XML schema. This is more complex than a property based configuration but has the advantages of:

  • Protects you from making typo errors
  • Allows you to refactor your code without a need for changes in configuration files, and
  • Enables you to mark some properties as required so that XML schema validation will prevent you from forgetting some parameters

This approach is intended to be used mainly if you provide your requirements for other teams or components.

Example

Let's say you have many configuration options or you want to use the same user requirements in several configuration files. You can create a simple XSD schema and read it using JAXB annotations all within a Red Deer configuration file:

Configuration File

<?xml version="1.0" encoding="UTF-8"?>
<testrun 
	xmlns="http://www.jboss.org/NS/Req" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:user="http://www.jboss.org/NS/user-schema"
	xsi:schemaLocation="http://www.jboss.org/NS/Req RedDeerRequirements.xsd http://www.jboss.org/NS/user-schema user-schema.xsd">

	<requirements>
		<user:user-requirement name="user-requirement">
			<user:db-name>USERS_ADMINISTRATION</user:db-name>
			<user:ip>127.0.0.1</user:ip>
			<user:port>1111</user:port>
		</user:user-requirement>
	</requirements>
</testrun>

XSD schema (user-schema.xsd)

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        elementFormDefault="qualified"
        targetNamespace="http://www.jboss.org/NS/user-schema"
        xmlns:user="http://www.jboss.org/NS/user-schema"
        xmlns:rd="http://www.jboss.org/NS/Req">

        <!-- Import basic RedDeer requirements -->
        <xs:import namespace="http://www.jboss.org/NS/Req" schemaLocation="RedDeerSchema.xsd" />

        <!-- Specify user-requirement -->
        <xs:element name="user-requirement" type="user:userRequirementType" substitutionGroup="rd:abstractRequirement">
                <xs:annotation>
                        <xs:documentation>Specifies all data needed to create a user in the database</xs:documentation>
                </xs:annotation>
        </xs:element>

        <!-- type for user-requirement -->
        <xs:complexType name="userRequirementType">
                <xs:complexContent>
                       <xs:extension base="rd:abstractRequirementType">
                               <xs:sequence>
                                        <xs:element name="db-name" type="xs:string" minOccurs="1" maxOccurs="1"/>
                                        <xs:element name="ip" type="xs:string" minOccurs="1" maxOccurs="1"/>
                                        <xs:element name="port" type="xs:string" minOccurs="1" maxOccurs="1"/>
                                </xs:sequence>
                        </xs:extension>
                </xs:complexContent>
        </xs:complexType>
</xs:schema>

Requirement Usage

This is how you declare the requirement that user with name ''admin'' has to be present in the database:

@RunWith(RedDeerSuite.class)
@User(name="admin")
public class UserTest {
        @Test
        public void test(){

        }
}

Requirement Configuration

To read the configuration from the Red Deer configuration file using your own schema you need to create a configuration object with JAXB annotations:

@XmlRootElement(name="user-requirement", namespace="http://www.jboss.org/NS/user-schema")
public class UserConfiguration {

	private String dbName;

	private String ip;

	private String port;

	public String getIp() {
		return ip;
	}

	@XmlElement(namespace="http://www.jboss.org/NS/user-schema")
	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	@XmlElement(namespace="http://www.jboss.org/NS/user-schema")
	public void setPort(String port) {
		this.port = port;
	}

	public String getDbName() {
		return dbName;
	}

	@XmlElement(name="db-name", namespace="http://www.jboss.org/NS/user-schema")
	public void setDbName(String dbName) {
		this.dbName = dbName;
	}
}

Requirement

Since it is possible to use requirements without any configuration or with a property based configuration, you need to let Red Deer know that you'd like to use a custom configuration by implementing CustomConfiguration. You'll also need to indicate the class of JAXB annotated object (called Userconfiguration):

public class UserRequirement implements Requirement<User>, CustomConfiguration<UserConfiguration> {

	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface User {
		String name();
	}

	private User user;

	private UserConfiguration userConfiguration;

	public boolean canFulfill() {
		// return true if you can connect to the database
		return true;
	}

	public void fulfill() {
		// create an admin user in the database if it does not exist yet
	}

	public void setDeclaration(User user) {
		this.user = user;
	}

	public Class<UserConfiguration> getConfigurationClass() {
		return UserConfiguration.class;
	}

	public void setConfiguration(UserConfiguration config) {
		this.userConfiguration = config;
	}
}

Accessing XML configuration from within test class

It is also possible to inject the whole Requirement instance into the test class so that you are able to access the configuration defined in the XML file.

Example

You need to access user's password (defined in the XML configuration file) in your test. You can inject the whole UserRequirement instance into the test's property and access all data that the requirement exposes.

Configuration file

    <?xml version="1.0" encoding="UTF-8"?>
    <testrun
        xmlns="http://www.jboss.org/NS/Req"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.jboss.org/NS/Req RedDeerSchema.xsd">

        <requirements>
                <requirement class="org.jboss.reddeer.wiki.user.db.password.UserRequirement" name="userRequirement">
			<property key="name" value="USERS_ADMINISTRATION" />
			<property key="ip" value="127.0.0.1" />
			<property key="password" value="abc123" />
		</requirement>
        </requirements>
    </testrun>

Requirement Usage

This is how you inject the UserRequirement instance into the test and access the password:

@RunWith(RedDeerSuite.class)
@User(name="admin")
public class UserTest {
	
	@InjectRequirement
	private UserRequirement userRequirement;
	
	@Test
	public void test(){
		System.out.println(userRequirement.getPassword());
	}
}

Requirement

The UserRequirement needs to provide methods for accessing data (in this case getPassword()):

public class UserRequirement implements Requirement<User>, PropertyConfiguration{

	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface User {
		String name();
	}
	
	private User user;
	
	private String name;
	
	private String ip;
	
	private String password;
	
	public boolean canFulfill() {
		// return true if you can connect to the database
		return true;
	}

	public void fulfill() {
		user.name();
		// create an admin user in the database if it does not exist yet
	}
	
	@Override
	public void setDeclaration(User user) {
		this.user = user;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setIp(String ip) {
		this.ip = ip;
	}
	
	public void setPassword(String password) {
		this.password = password;
	}
	
	public String getPassword() {
		return password;
	}
}
Clone this wiki locally