Page History
...
Overall Test Results gadget
Tests List gadget
Tests report (legacy report on project tab)
The legacy Tests report, available on the project tab, can be configured to show additional sortable columns. Thus, you may add the risk level related field (or others) and sort by it.
Note: this report is still a bit limited, so no advanced/flexible filtering mechanisms are available.
...
If you're adopting risk at Test level, you may create several saved filters (e.g. "tests_risk_low", "tests_risk_medium", "tests_risk_high", "tests_risk_very_high", "tests_risk_severe") as this can later be quite useful, namely for using within gadgets or reports.
Risk at requirement or at project level
...
Create a set of filters, each one related with a different risk level. Using a nomenclature for the saved filters can help you out. Example: "RR_risks_low", "RR_risks_medium", "RR_risks_high", "RR_risks_extreme".
To make our life easier, we may also use saved filters to group Tests by the indirectly associated risk revel, using JQL and the requirementTests() function.
You won't need these pre-created filters to pick tests sorted by risk level whenever adding Tests to a Test Execution/Test Plan as you may write JQL query inline; however, they will be required if you aim to filter out Tests in gadgets and in reports by the related risk level.
...
After choosing the type of fields you'll need, adding/creating them is straightforward; this can be done right from the issue screen using Admin > Add field.
It can also be done from Jira's administration, under Issues > Custom fields > Add custom field.
Configuration examples
...
If you use a "Select List" then you need to make sure their option values are sorted in the right order and that their values are also alphabetically sorted in the same way.
In the previous example, 0 is redundant with having an empty value; thus, you may either set the default to be 0 (instead of "none") or remove the "0" option.
...
You may create a custom field, either "Number" or "Select List (single choice)" based, to store and manage your risk. A "Select List" may provide additional benefits.
No matter your choice, the risk level field won't be automatically calculated based on the probability/impact fields; you'll need to use a app or custom development for that as shown in the next sections.
...
To configure Risk Management for Jira, go to Add-ons > Risk Management > Required > Custom Fields.
In the previous screen you can choose to reuse existing fields for abstracting the probability and the impact (they should be "Select List" based).
It can also create these fields for you as well, along with the risk level. Choose "Add Custom Fields" and the name for the impact (i.e. consequence), probability and risk level (i.e. risk index) fields; please check "RiskIndexNumberField" flag.
Info | ||
---|---|---|
| ||
If you already had values for probability and impact/consequence, indexing will not recalculate the risk level, as the app uses a listener to update it upon changes. |
...
On issues having the impact/consequence and probability related fields, risk level ("Risk Score" on the following example) will be updated upon update on any of these fields.
Within the Risk Matrix, issues can easily be found/searched as they're mapped to a cell and to a specific color, depending on their probability, impact/consequence and calculated risk level.
In Xray, whenever adding Tests to a Test Execution for example, the risk level field (e.g "Risk Score") can later be used to sort Tests ascending/descending; it can also be used as a filter.
Using Risk Register app
...
- configure Risk Register (fields, screens, issue type)
- configure global settings of the app (Add-ons > Risk Register > Settings), including the custom fields used by Risk Register, which are always created (you may change their name afterwards). If you have existing custom fields with similar names, new ones will be created (no existing fields will or can be reused).
You need also to define which issue type will be used to abstract the risk; Risk Register can create one for you (e.g. "Risk").
- enable risk management for your project, either from the project settings "Risk register" section or from Jira administration > Add-ons > Risk Register > Risk registers.
- configure global settings of the app (Add-ons > Risk Register > Settings), including the custom fields used by Risk Register, which are always created (you may change their name afterwards). If you have existing custom fields with similar names, new ones will be created (no existing fields will or can be reused).
- in Xray, configure Risk as a "requirement issue type" (i.e. a coverable issue type that you can cover with test cases)
- Some of the risks that you have identified during risk assessment may be covered with tests. Therefore, they can be handled similar to "requirements" in Xray. Just go to your Jira administration > Add-ons > Xray > Issue Type Mapping and drag the Risk issue type to the "Requirement Issue Types" column and save it. The project containing Risk issues must be enabled as a requirements project, either from Jira administration > Add-ons > Xray > Requirement Projects or from the project settings page, within the Summary section, using the action "Enable Xray Requirement Coverage."
- Some of the risks that you have identified during risk assessment may be covered with tests. Therefore, they can be handled similar to "requirements" in Xray. Just go to your Jira administration > Add-ons > Xray > Issue Type Mapping and drag the Risk issue type to the "Requirement Issue Types" column and save it. The project containing Risk issues must be enabled as a requirements project, either from Jira administration > Add-ons > Xray > Requirement Projects or from the project settings page, within the Summary section, using the action "Enable Xray Requirement Coverage."
...
Using it
Some of the identified project level risks may be mitigated with tests while other ones may not.
For those that can, you may start by defining the "Risk assessment" related fields, including probability and impact, in the Risk issues (i.e. the ones whose issue type is configured to be handled as risk).
Later on, we'll need to find these Tests based on the "exposure" of the associated/covered risk. To make our life easier, we may use saved filters to group Tests by the indirectly associated risk exposure.
Create a set of filters, each one related with a different "Exposure" (i.e risk level). Using a nomenclature for the saved filters can help you out. Example: "RR_risks_low", "RR_risks_medium", "RR_risks_high", "RR_risks_extreme".
To obtain the related Tests, you can use JQL and the requirementTests() function.
Thus, lets say you want to obtain all Tests related with Risks with an "High" exposure in order to add them to an existing Test Execution; you can do as follows.
The returned Tests may have different relevance; therefore, if you want, you may use a complementary field (e.g. "Priority") to sort them out.
...
For the calculation of risk you may use a Calculated Single-select" field (recommended) or a "Calculated Number" field from JMCF.
Note |
---|
After creating the calculated field, you'll need to perform re-indexing if your project already has values for probability and impact. |
...
Using a single-select field will ease the search of issues later on.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
impact = issue.get("Impact") probability = issue.get("Probability") def risk def level if ((impact) && (probability)) { risk = Double.parseDouble(impact) * Double.parseDouble(probability) } else { risk = null } if ((risk>0) && (risk<4)) { level = "L1: Low" } else if ((risk>=4) && (risk<8)) { level = "L2: Medium" } else if ((risk>=8) && (risk<12)) { level = "L3: High" } else if ((risk>=12) && (risk<16)) { level = "L4: Very High" } else if (risk==16) { level = "L5: Severe" } else { level = null } return level |
...
After creating the previous fields, you may use it, for example, in the dialog for adding Tests to an existing Test Execution; you can also easily filter issues by risk level.
Calculating risk and storing it in a Calculated Number field
In this approach, risk is calculated and stored in a Calculated Number field; we just need to define the groovy script that will be used to calculate it.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
impact = issue.get("Impact") probability = issue.get("Probability") def risk if ((impact) && (probability)) { risk = Double.parseDouble(impact) * Double.parseDouble(probability) } else { risk = null } return risk |
...
- create a standard custom field (e.g. "Risk Level") of type "Select List (single choice)," having the options corresponding to the different risk level bands correctly ordered (e.g. "L1: Low", "L2: Medium", "L3: High", "L4: Very High", "L5: Severe"). Make sure the option values maintain their order if they're sorted alphabetically (this will help you later on);
- using ScriptRunner, go to you Jira administration and create accustom Script Field (Add-ons > ScriptRunner > Script Fields > Add New Item > Custom Script Field).
...
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.issue.CustomFieldManager import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.Issue import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.event.issue.IssueEvent import com.atlassian.jira.issue.util.DefaultIssueChangeHolder import com.atlassian.jira.issue.ModifiedValue import com.atlassian.jira.issue.history.ChangeItemBean import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.issue.customfields.option.Option import com.atlassian.jira.event.type.EventDispatchOption import com.atlassian.jira.issue.index.IssueIndexingService import com.atlassian.jira.util.ImportUtils import org.apache.log4j.Logger import org.apache.log4j.Level def log = Logger.getLogger("com.onresolve.jira.groovy") log.setLevel(Level.DEBUG) MutableIssue issue = event.issue def issueType = issue.issueTypeObject.name // Only process Test or Story based issues (ADAPT IT tou your needs) if (issueType != "Test" && issueType != "Story"){ return } String cfImpactName = "Impact" String cfProbabilityName = "Probability" String cfRiskLevelName = "Risk Level" def changeManager = ComponentAccessor.getChangeHistoryManager(); def changeLog = event.getChangeLog(); def impactChanged = false, probabilityChanged = false; if ( changeLog != null ) { def changeId = changeLog.getLong( "id" ); if ( changeId != null ) { def change = changeManager.getChangeHistoryById( changeId ); for (ChangeItemBean bean : change.getChangeItemBeans() ) { if ( bean.getField().equals(cfImpactName) && ChangeItemBean.CUSTOM_FIELD.equals( bean.getFieldType() )) { log.debug(cfImpactName + " changed"); impactChanged = true; } if ( bean.getField().equals(cfProbabilityName) && ChangeItemBean.CUSTOM_FIELD.equals( bean.getFieldType() )) { log.debug(cfProbabilityName + " changed"); probabilityChanged = true; } } } } if (impactChanged || probabilityChanged){ def issueManager = ComponentAccessor.issueManager def customFieldManager = ComponentAccessor.customFieldManager def cfImpact = customFieldManager.getCustomFieldObjectByName(cfImpactName) def cfProbability = customFieldManager.getCustomFieldObjectByName(cfProbabilityName) def option def impact def probability option = (Option)issue.getCustomFieldValue(cfImpact) if (option != null) impact = option.getValue() option = (Option)issue.getCustomFieldValue(cfProbability) if (option != null) probability = option.getValue() def risk def level if ((impact) && (probability)) { risk = Double.parseDouble(impact) * Double.parseDouble(probability) } else { risk = null } if ((risk>0) && (risk<4)) { level = "L1: Low" } else if ((risk>=4) && (risk<8)) { level = "L2: Medium" } else if ((risk>=8) && (risk<12)) { level = "L3: High" } else if ((risk>=12) && (risk<16)) { level = "L4: Very High" } else if (risk==16) { level = "L5: Severe" } else { level = null } def cfRiskLevel = customFieldManager.getCustomFieldObjectByName(cfRiskLevelName) def fieldConfig = cfRiskLevel.getRelevantConfig(issue) def value = ComponentAccessor.optionsManager.getOptions(fieldConfig)?.find { it.toString() == level } issue.setCustomFieldValue(cfRiskLevel, value) def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() issueManager.updateIssue(currentUser, issue, EventDispatchOption.DO_NOT_DISPATCH, false); def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService) boolean wasIndexing = ImportUtils.isIndexIssues() ImportUtils.setIndexIssues(true) issueIndexingService.reIndex(issueManager.getIssueObject(issue.id)) ImportUtils.setIndexIssues(wasIndexing) log.debug(value) } |
...
Upon creation, you'll be warned about configuring the context and screen, for choosing to which issue types it's applicable, and to the screens where you want it visible.
Using it
As an example, you may use the previous field as a way to sort and filter Tests in the "Add Tests" dialog, whenever adding them from within an existing Test Execution.
Calculating risk at requirement or project level and storing it in a Single Select field at Test level using a Script Listener
...
As changes are made to probability or to impact on the parent issue (e.g. requirement, project risk,) a listener will handle the issue updated & created events and will update a "Select List (single choice)" based custom field defined for Test issues. That field needs to be created and configured beforehand will all the possible options.
Info | ||
---|---|---|
| ||
This approach and the code of following script assumes that a Test covers just one issue. If, for example, a given requirement is covered by a Test that also covers another requirement, then whenever editing the impact in one of the requirements will update/overwrite the calculated risk level on the custom field on the Test issue. Thus, you may have to think if you want to follow this approach as it has some constraints. |
...
In this example, we're calculating the risk as a number using a custom Script Field (Add-ons > ScriptRunner > Script Fields > Add New Item > Custom Script Field).
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.component.ComponentAccessor import org.apache.log4j.Logger import org.apache.log4j.Level def log = Logger.getLogger("com.onresolve.jira.groovy") log.setLevel(Level.DEBUG) def issueManager = ComponentAccessor.issueManager def impact = getCustomFieldValue("Impact").toString() def probability = getCustomFieldValue("Probability").toString() def risk if ((impact!="null") && (probability!="null")) { risk = Double.parseDouble(impact) * Double.parseDouble(probability) } else { risk = null } return risk |
...
Info | ||
---|---|---|
| ||
Even if risk value related custom field is created using the Template "Number Field", as shown earlier, sorting won't work as expected as it will be done by text comparison (i.e. alphabetically). You will need to to guarantee that Jira uses the "Number Searcher" by editing the custom field details. If you change this, don't forget to re-index. |
Calculating risk as text using a Script Field
...