Youtrack logo

The YouTrack Blog

The Issue Tracking and Project Management Tool for Teams

Best Practices Features Newsletter

Make It Workflow — Parte 13: Compatibilidad con escenarios de gestión de pruebas

¡Bienvenidos de nuevo a nuestra serie Make It Workflow! Hemos recibido varias peticiones de la comunidad para que hablemos de las funcionalidades de YouTrack que puede utilizar el equipo de QA (Quality Assurance). Hoy nos gustaría echar un vistazo a cómo efectuar el mantenimiento de los escenarios de gestión de pruebas (TMS, test management scenarios) en YouTrack. Este artículo le mostrará cómo configurar YouTrack para que pueda mantener la funcionalidad de gestión de pruebas dentro del propio YouTrack, sin tener que utilizar soluciones de terceros. Esto le ayuda a ahorrar en costes de licencias y hace que el proceso de gestión de pruebas fluya mejor.

Este artículo está destinado a gestores de pruebas y a cualquiera interesado en flujos de trabajo de YouTrack. Encontrará un ejemplo práctico de cómo crear un proceso de pruebas en YouTrack, con recomendaciones de ajustes para proyectos e incidencias, una descripción de cómo implementar escenarios de gestión de pruebas, y mucho más.

También hemos preparado una lista de bloques de código de flujos de trabajo listos para usar, que puede emplear para automatizar procesos de gestión de pruebas. Estos bloques de código facilitan la asociación de casos de prueba con la operación de pruebas específica, clonación de operación de pruebas, mostrar sugerencias del sistema en la prueba siguiente, y más.

Ajustes para proyectos de gestión de pruebas en YouTrack

Quizá desee utilizar tipos de incidencias específicos para la gestión de pruebas en
sus proyectos de gestión de pruebas, como Test Case (caso de pruebas), Test Suite (un grupo de casos de pruebas), Test Run (un conjunto de casos de pruebas o de grupos de casos asignados a un ciclo de pruebas específico) y Test Case Executions (ejecuciones de casos de pruebas) asignados a una Test Run (operación de pruebas) específica. Tendrá que configurar los tipos de incidencias necesarios para su proyecto TMS. Hay 3 tipos de enlaces a incidencias para establecer conexiones y relevancia entre sus tareas de gestión de pruebas:

  • Las “parent-subtasks” (o subtareas primarias) estándar mantienen operaciones de pruebas y ejecuciones de casos de pruebas, así como grupos de pruebas y relaciones de casos de pruebas.
  • La “Execution” (ejecución) personalizada mantiene las relaciones de ejecución de casos de pruebas y los casos de pruebas.
  • El “Related Bug” (error relacionado) personalizado mantiene las relaciones entre errores asignados y pruebas fallidas.

Los requisitos de prueba se pueden mantener como artículo vinculado a las incidencias de un campo de texto.
Además, para sus tareas de gestión de pruebas, quizá desee configurar campos personalizados como Modo de prueba, Categoría, Flujo de prueba y Aplicación, junto con valores predefinidos (p. ej. estados de pruebas predefinidos). También puede utilizar campos condicionales cuando tiene que mostrar información específicamente para un tipo de incidencia.

Puede ajustarse un conjunto de campos predefinidos para los tipos de incidencia “Test Case” y “Test Run” según las necesidades de su empresa.

Configuración de operaciones de pruebas y ejecución de pruebas

Cuando, por ejemplo, deseamos probar una versión específica de un producto, tenemos que crear una operación de pruebas y asignarle un número de grupos de pruebas y casos de pruebas relevantes. Estos son los pasos a seguir para hacerlo:

  • Cree una incidencia con el tipo de incidencia “Test Run”.
  • Use the ‘Assigned test case and test suite’ custom link type to link to the issue.
  • En la ventana emergente, especifique los casos de pruebas que le gustaría incluir en la operación de pruebas.

Los pasos del flujo de trabajo ‘”Populate Test Run” le ahorrarán tiempo y esfuerzo a su equipo de QA:

  • Se crearán en el sistema copias de todos los grupos de pruebas y casos de pruebas seleccionados. Todas las incidencias serán del tipo “Test Case Execution”.
  • El tipo de enlace “Execution” conectará ejecuciones de casos de pruebas con casos de pruebas seleccionados.
  • Las incidencias nuevas que se creen se vincularán con la operación de prueba como incidencia primaria y sus subtareas correspondientes.

Así funciona:

Ampliar bloque de código

var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');

exports.rule = entities.Issue.onChange({
  title: 'Populate-test-run',
  guard: function(ctx) {
    var issue = ctx.issue;
    return !issue.isChanged('project') && issue.Type && (issue.Type.name == ctx.Type.TestRun.name) && issue.links[ctx.Execution.outward].added.isNotEmpty() && issue.isReported;
  },
  action: function(ctx) {
    var issue = ctx.issue;
    var totalTestRuns = issue.links[ctx.Execution.outward].added.size;
    issue.links[ctx.Execution.outward].added.forEach(function(TestCase) {
      TestCase.links[ctx.Execution.inward].delete(issue);

      var TestCaseRun = TestCase.copy();
      TestCaseRun.Type = ctx.Type.TestExecution.name;
      TestCaseRun.Status = ctx.Status.NoRun.name;
      Object.keys(TestCaseRun.links).forEach(function(linkType) {
       if (!TestCaseRun.links[linkType])
        return;
         TestCaseRun.links[linkType].clear();
      });
      TestCaseRun.summary = "[TEST_CASE_EXECUTION" + "] [" + TestCaseRun.summary + "]";

      TestCaseRun.links[ctx.Subtask.inward].add(issue);
      issue.links[ctx.Subtask.outward].add(TestCaseRun);
      TestCaseRun.links[ctx.Execution.outward].add(TestCase);
    });
    issue.fields['Total number of test cases'] = totalTestRuns;
  },
  requirements: {
    Execution: {
      type: entities.IssueLinkPrototype,
      name: 'Execution',
      inward: 'Execution',
      outward: 'Assigned test case or test suite'
    },
    Subtask: {
      type: entities.IssueLinkPrototype,
      name: 'Subtask',
      inward: 'parent for',
      outward: 'subtask of'
    },
    Type: {
      type: entities.EnumField.fieldType,
      TestExecution: {
        name: "Test Case Execution"
      },
      TestRun: {
        name: "Test Run"
      },
      TestCase: {
        name: "Test Case"
      },
      TestSuite: {
        name: "Test Suite"
      }
    },
    Total: {
      type: entities.Field.integerType,
      name: 'Total number of test cases'
    },
    TotalFailed: {
      type: entities.Field.integerType,
      name: 'Number of failed test cases'
    },
    TotalPassed: {
      type: entities.Field.integerType,
      name: 'Number of passed test cases'
    },
    Status: {
      type: entities.EnumField.fieldType,
      InProgress: {
        name: 'In Progress'
      },
      Passed: {
        name: 'Passed'
      },
      Failed: {
        name: 'Failed'
      },
      NoRun: {
        name: 'No Run'
      },
    },
  }
});

Mantenimiento de actividades de prueba

Cuando las operaciones de pruebas estén totalmente configuradas, un ingeniero de pruebas puede comenzar a realizarlas. De forma predeterminada, todas las incidencias de tipo “Test Case Execution” y “Test Run” se encuentran en estado “No Run”.

Cómo cambiar entre pruebas

Cuando trabaja con una serie de pruebas, tiene dos modos de cambiar de una prueba a otra:

  1. Cambie manualmente el estado de la prueba en la página de la lista de incidencias.
  2. Abra una prueba y cambie a la siguiente prueba no completada. When you want to switch tests in this way, a helpful pop-up appears that suggests the next test to run.
    The following actions can be automated:

    • Compruebe si hay pruebas en estado “No Run” y pertenecen a la misma operación de pruebas.
    • Muestre un mensaje con la URL de la siguiente prueba disponible (si hay alguna).

      ApplyingCommandForSubsystem

    Esto puede realizarse utilizando el código siguiente.

    Ampliar bloque de código

     action: function(ctx) {
      var issue = ctx.issue;
      if (!issue.links['subtask of'].isEmpty()) {
                  var parent = issue.links['subtask of'].first();
                  var TestRunList = parent.links[ctx.Subtask.outward];
                  var resultSet = null;
                  var isPassing = true;
                  TestRunList.forEach(function(v) {
                    if (v.Status.name == ctx.Status.Failed.name) {
                      isPassing = false;
                    } else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
                      resultSet = v;
                    }
                  });
                  if (resultSet) {
                    var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
                    var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
                    workflow.message(message);
        }
    }
    }
    

Estados de las pruebas

Al cambiar de una prueba a otra, un ingeniero de pruebas puede indicar que la prueba ha quedado “Passed” (Superada) o cambiar el estado de la prueba a “Failed” (Fallida) (en el caso de que se haya identificado un error durante la ejecución del caso de prueba). A partir de los ajustes de YouTrack que hemos configurado inicialmente para este proyecto, hay varios estados de operaciones de pruebas predefinidos:

  • Failing: al menos una de las pruebas asociadas se encuentra en estado “No Run” y al menos una de las pruebas asociadas se encuentra en estado “Failed”.
  • Passing: al menos una de las pruebas asociadas se encuentra en estado “No Run” y no hay pruebas asociadas en estado “Failed”.
  • Failed: no hay ninguna prueba asociada en estado “No Run” y al menos una de las pruebas asociadas se encuentra en estado “Failed”.
  • Passed: ninguna prueba asociada se encuentra en estado “No Run” o “Failed”.

Los estados listados anteriormente pueden ayudar a su equipo de QA a monitorizar los progresos de las pruebas a través de todo un ciclo de pruebas. Es importante tener en cuenta que el estado de la operación de pruebas depende solo de las pruebas asociadas y no debería cambiarse manualmente. El proceso de determinar estados de operaciones de pruebas se implementa utilizando el flujo de trabajo state-machines per issue type. El bloque de código para cambiar entre pruebas también se incorpora en el flujo de trabajo.

Ampliar bloque de código


var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');

exports.rule = entities.Issue.stateMachine({
  title: 'status-management',
  stateFieldName: 'Status',
  typeFieldName: 'Type',
  defaultMachine: {
    'No Run': {
      initial: true,
      transitions: {
        'Failed': {
          targetState: 'Failed',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
              var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var resultSet = null;
              var isPassing = true;
              TestRunList.forEach(function(v) {
                if (v.Status.name == ctx.Status.Failed.name) {
                  isPassing = false;
                } else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
                  resultSet = v;
                }
              });
              if (resultSet) {
                var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
                var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
                workflow.message(message);
                // Updating Test Run Status 
                parent.fields["Status"] = ctx.Status.Failing;
              } else {
                parent.fields["Status"] = ctx.Status.Failed;
              }
            }
          }
        },
        'Passed': {
          guard: function(ctx) {
            var issue = ctx.issue;
            return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
          },
          targetState: 'Passed',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
              var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var resultSet = null;
              var isPassing = true;
              TestRunList.forEach(function(v) {
                if (v.Status.name == ctx.Status.Failed.name) {
                  isPassing = false;
                } else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
                  resultSet = v;
                }
              });
              if (resultSet) {
                var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
                var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
                workflow.message(message);
                parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
              } else {
                parent.fields["Status"] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;
              }
            }
          }
        }
      }
    },
    Passed: {
      transitions: {
        'Failed': {
          guard: function(ctx) {
            var issue = ctx.issue;
            return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
          },
          targetState: 'Failed',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
             var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var resultSet = null;
              TestRunList.forEach(function(v) {
                if (v.Status.name == ctx.Status.Failed.name) {
                } else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
                  resultSet = v;
                }
              });
              if (resultSet) {
                var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
                var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
                workflow.message(message);
                parent.fields["Status"] = ctx.Status.Failing;
              } else {
                parent.Status = ctx.Status.Failed;
              }
            }
          }
        },
        'No Run': {
          guard: function(ctx) {
            var issue = ctx.issue;
            return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
          },
          targetState: 'No Run',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
              var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var ActiveTestRun = false;
              var isPassing = true;
              TestRunList.forEach(function(v) {
                if (v.Status.name == ctx.Status.Failed.name) {
                  isPassing = false;
                  ActiveTestRun = true;
                } else if ((v.Status.name == ctx.Status.Passed.name) && (v.id !== issue.id)) {
                  ActiveTestRun = true;
                }
              });
              if (ActiveTestRun) {
                parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
              } else parent.fields["Status"] = ctx.Status.InProgress;
            }
          }
        }
      }
    },
    Failed: {
      transitions: {
        'Passed': {
          guard: function(ctx) {
            var issue = ctx.issue;
            return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
          },
          targetState: 'Passed',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
              var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var resultSet = null;
              var isPassing = true;
              TestRunList.forEach(function(v) {
                if ((v.Status.name == ctx.Status.Failed.name) && (v.id !== issue.id)) {
                  isPassing = false;
                } else if ((v.Status.name == ctx.Status.InProgress.name) && (v.id !== issue.id)) {
                  resultSet = v;
                }
              });
              if (resultSet) {
                var otherIssueLink = '<a href="' + resultSet.url + '"> ' + resultSet.id + '</a>';
                var message = 'Switch to next open test in current Test Run' + otherIssueLink + '.';
                workflow.message(message);

                parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
              } else {
                parent.fields["Status"] = (isPassing) ? ctx.Status.Passed : ctx.Status.Failed;
              }
            }
          }
        },
        'No Run': {
          guard: function(ctx) {
            var issue = ctx.issue;
            return !issue.isChanged('project') && !issue.becomesReported && issue.isReported && (issue.Type.name == ctx.Type.TestExecution.name);
          },
          targetState: 'No Run',
          action: function(ctx) {
            var issue = ctx.issue;
            if (!issue.links['subtask of'].isEmpty()) {
              var parent = issue.links['subtask of'].first();
              var TestRunList = parent.links[ctx.Subtask.outward];
              var ActiveTestRun = false;
              var isPassing = true;
              TestRunList.forEach(function(v) {
                if ((v.Status.name == ctx.Status.Failed.name) && (v.id !== issue.id)) {
                  isPassing = false;
                  ActiveTestRun = true;
                } else if ((v.Status.name == ctx.Status.Passed.name) && (v.id !== issue.id)) {
                  ActiveTestRun = true;
                }
              });
              if (ActiveTestRun) {
                parent.fields["Status"] = (isPassing) ? ctx.Status.Passing : ctx.Status.Failing;
              } else parent.fields["Status"] = ctx.Status.InProgress;
            }
          }
        }
      }
    }
  },
  alternativeMachines: {
    'Test Run': {
      'No Run': {
        initial: true,
        transitions: {
          'Failing': {
            targetState: 'Failing',
            action: function(ctx) {
           /* Add actions. */
            }
          },
          'Failed': {
            targetState: 'Failed',
            action: function(ctx) {
         /* Add actions. */
            }
          },
          'Passing': {
            targetState: 'Passing',
            action: function(ctx) {
          /* Add actions. */
            }
          },
          'Passed': {
            targetState: 'Passed',
            action: function(ctx) {
           /* Add actions. */
            }
          }
        }
      },
      Failing: {
        transitions: {
          'Passing': {
            targetState: 'Passing',
            action: function(ctx) {
           /* Add actions . */
            }
          },
          'Passed': {
            targetState: 'Passed',
            action: function(ctx) {
          /* Add actions. */
            }
          },
          'Failed': {
            targetState: 'Failed',
            action: function(ctx) {
           /* Add actions. */
            }
          }
        }
      },
      Passing: {
        transitions: {
          'Failing': {
            targetState: 'Passing',
            action: function(ctx) {
              workflow.check(false, workflow.i18n('Test Run has-read-only status which is defined based on assigned tests statuses'));
            }
          },
          'Passed': {
            targetState: 'Passed',
            action: function(ctx) {
           /* Add actions. */
            }
          },
          'Failed': {
            targetState: 'Failed',
            action: function(ctx) {
           /* Add actions. */
            }
          }
        }
      },
      Failed: {
        transitions: {
          'Passing': {
            targetState: 'Passing',
            action: function(ctx) {
           /* Add actions. */
            }
          },
          'Passed': {
            targetState: 'Passed',
            action: function(ctx) {
           /* Add actions. */
            }
          },
          'Failing': {
            targetState: 'Failed',
            action: function(ctx) {
           /* Add actions. */
            }
          }
        }
      },
      Passed: {
        transitions: {
          'Passing': {
            targetState: 'Passing',
            action: function(ctx) {
           /* Add actions. */
            }
          },
          'Failed': {
            targetState: 'Passed',
            action: function(ctx) {
          /* Add actions. */
            },
          },
          'Failing': {
            targetState: 'Failed',
            action: function(ctx) {
           /* Add actions. */
            }
          }
        }
      }
    }
  },
  requirements: {
    Assignee: {
      type: entities.User.fieldType
    },
    Status: {
      type: entities.EnumField.fieldType,
      InProgress: {
        name: 'No Run'
      },
      Failing: {
        name: 'Failing'
      },
      Passing: {
        name: 'Passing'
      },
      Passed: {
        name: 'Passed'
      },
      Failed: {
        name: 'Failed'
      },
    },
    Type: {
      type: entities.EnumField.fieldType,
      TestRun: {
        name: "Test Run"
      },
      TestExecution: {
        name: "Test Case Execution"
      }
    },
    Subtask: {
      type: entities.IssueLinkPrototype,
      name: 'Subtask',
      inward: 'subtask of',
      outward: 'parent for'
    },
  }
});
 

Estadísticas de pruebas

Puede que también le interesen las estadísticas de ciclos de pruebas clave. Para esta demostración hemos incorporado las métricas siguientes:

  • Número total de pruebas asignadas a una operación de prueba específica.
  • Número de pruebas en estado “Passed”.
  • Número de pruebas en estado “Failed”.


Gracias al código siguiente, todas las métricas se actualizan en caso de cualquiera de los cambios siguientes: cambios en el estado de las pruebas, asignación de una nueva prueba a la operación de pruebas, y eliminación de una prueba de una operación de pruebas.

Ampliar bloque de código

  exports.calculateStatuses = function(parent) {
  var totalTR = 0;
  var totalFailed = 0;
  var totalPassed = 0;
  if (!parent.links['parent for']) {
    return;
  } else {
    parent.links['parent for'].forEach(function(tr) {
      totalTR++;
      if (tr.Status.name == 'Passed') {
        totalFailed++;
      }
      if (tr.Status.name == 'Failed') {
        totalPassed++;
      }
    });
    parent.fields['Total number of test cases'] = totalTR;
    parent.fields['Number of passed test cases'] = totalPassed;
    parent.fields['Number of failed test cases'] = totalFailed;
    return true;
  }
};
exports.resetStatuses = function(testRun, testRunCopy) {
   testRunCopy.fields['Total number of test cases'] = testRun.fields['Total number of test cases'];
   testRunCopy.fields['Number of passed test cases'] = 0;
   testRunCopy.fields['Number of failed test cases'] = 0;
  return true;
};

En nuestro caso, este código se activa en varias reglas de flujo de trabajo, como “Update stats when links are adjusted”, “Switch to the next test case”, y otras. Si desea adoptar una serie de métricas que se adapten a sus necesidades específicas, puede añadir los campos personalizados necesarios y ajustar la lógica de su flujo de trabajo.

Funcionalidades de la herramienta de seguimiento de incidencias

Quizá desee crear una tarea por separado que trate un error encontrado en una prueba fallida y vincularla a una incidencia que identifique la ejecución de caso de pruebas correspondiente. Puede vincular pruebas fallidas con sus errores correspondientes utilizando un tipo de enlace de incidencia personalizado, o utilizar un campo de texto personalizado que contenga una referencia al error. Podría resultar más fácil organizar un proyecto de YouTrack por separado para efectuar un seguimiento de los errores.

Posibles mejoras

Puede ampliar las funciones de YouTrack para cumplir requisitos adicionales de la empresa. Estas son varias opciones que podría tener en cuenta.

Clonar una operación de pruebas existente

En ocasiones podría resultarle muy útil efectuar una operación de pruebas nueva que sea similar a una existente, pero que incluya algunos pequeños cambios (p. ej. un cambio en la versión asignada). El elemento de menú “Create Test Run copy” se ha creado específicamente para esos casos. Este elemento del menú está disponible solo para incidencias de tipo “Test Run”, y desencadena las acciones siguientes:

  • Crear una copia de la operación de pruebas, así como copias de las ejecuciones de casos de pruebas asignadas a ella.
  • All newly created test case execution copies are assigned to the new test run using the ‘parent-child’ issue link type. Además, estas copias se asignarán a los casos de pruebas originales con el tipo de enlace de incidencia “Execution” (para mantener la trazabilidad).
  • All statuses and statistics will be discarded for the newly created issue.

La lógica anterior se ha implementado utilizando la regla de acción “Create Test Run copy”.

Ampliar bloque de código

var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var utils = require('../calculate-tms-stats/utils');

exports.rule = entities.Issue.action({
  title: 'Create Test Run copy',
  command: 'Test Run Creation',
  guard: function(ctx) {
    return ctx.issue.isReported && (ctx.issue.Type.name == ctx.Type.TestRun.name);
  },
  action: function(ctx) {
    var issue = ctx.issue;
    var TestRunCopy = issue.copy(issue.project);
    TestRunCopy.Status = ctx.Status.InProgress; 
    var oldTestList = issue.links[ctx.Subtask.outward];
    oldTestList.forEach(function(v) {
      var newTest = v.copy(v.project);
      newTest.Status = ctx.Status.InProgress;
      newTest.links[ctx.Subtask.inward].delete(issue);
      newTest.links[ctx.Subtask.inward].add(TestRunCopy);
    });
    utils.resetStatuses(issue, TestRunCopy); 
    var newTestRunLink = '<a href="' + TestRunCopy.url + '"> ' + TestRunCopy.id + '</a>';
    var message = 'New Test Run has been created ' + newTestRunLink + '.';
    workflow.message(message);
  },
  requirements: {
    Execution: {
      type: entities.IssueLinkPrototype,
      name: 'Execution',
      inward: 'Execution',
      outward: 'Assigned Test case or test suite'
    },
    Subtask: {
      type: entities.IssueLinkPrototype,
      name: 'Subtask',
      inward: 'subtask of',
      outward: 'parent for'
    },
    Type: {
      type: entities.EnumField.fieldType,
      TestRun: {
        name: "Test Run"
      },
    },
    Status: {
      type: entities.EnumField.fieldType,
      InProgress: {
        name: 'No Run'
      },
    }
  }
});
 

Restringir las acciones de los usuarios

Para evitar potenciales equivocaciones al trabajar con escenarios de pruebas (p. ej. el uso de los tipos de incidencias erróneos), puede restringir posibles acciones de los usuarios. Por ejemplo, puede implementar acciones para asegurarse de que los usuarios vinculen casos de pruebas a operaciones de pruebas. When the user links a test run to an issue, this action will check whether the linked issue type is ‘Test Case’ or ‘Test Suite.’ Si no es ninguno de los dos, la acción enviará una advertencia que evita que el usuario continue.

El siguiente bloque de código puede añadirse al flujo de trabajo “populate test run”.

Ampliar bloque de código

 var message = '<a href="' + TestCase.url + '"> ' + TestCase.id + '</a>';
      workflow.check((TestCase.Type.name == ctx.Type.TestCase.name) || (TestCase.Type.name == ctx.Type.TestSuite.name), workflow.i18n('\'Test Run\' can be linked to \'Test Case\' and \'Test Suite\' only, but {0} has \'{1}\' type!', message, TestCase.Type.name));
    

Como se ha indicado anteriormente, el estado de la operación de pruebas debería ser de solo lectura, puesto que depende del progreso de las ejecuciones de las pruebas. Esto se puede lograr de un modo sencillo simplemente incluyendo un bloque de código que restrinja los cambios de estado manuales para incidencias de tipo “Test Run”.


Por ejemplo, la sección ‘transitions’ del tipo de incidencia ‘Test Run’ en el flujo de trabajo “status-management” se puede ajustar del modo siguiente:

Ampliar bloque de código

   'Failing': {
            targetState: 'Failing',
            action: function(ctx) {
              workflow.check(false, workflow.i18n('Test run has read-only status which is defined based on assigned tests statuses'));
            }
          },
          ...
        }

Informes y trazabilidad

Creando informe

Una vez finalizado el ciclo de pruebas, probablemente desee analizar los resultados. Para ello puede utilizar la funcionalidad de informes de YouTrack. You can select the report type, which will display key information related to the test management procedure. Para esta demostración incluiremos 2 informes en el panel:

  • Informe de flujo acumulativo con filtrado por campo “version”. Este tipo de informe de estado hace referencia a una versión de producto específica. Le muestra métricas clave, como el número de pruebas en estado “Passed”, “Failed” y “No Run”, distribuídas en una línea temporal. Para mostrar datos de múltiples versiones, deberá crear un informe para cada versión. Podría resultar práctico añadir todos los informes al panel para que pueda trabajar con todos los datos en un solo lugar.

    Configuración del informe:

    ApplyingCommandForSubsystem

    Resultados del informe:

    ApplyingCommandForSubsystem

  • Informe de distribución de incidencias. Este informe es una instantánea que incluye métricas clave (como el número de operaciones de pruebas con cada estado) para varias versiones del producto. Específicamente para esta demo hemos incluido resultados de informes que pueden ayudarle a comprar versiones entre sí y analizar la estabilidad de las mismas.

    Configuración del informe:

    ApplyingCommandForSubsystem

    Resultados del informe:

    ApplyingCommandForSubsystem

Trazabilidad

Como YouTrack almacena información clave relacionada con los ciclos de pruebas para cada incidencia de tipo “Test Execution”, siempre podrá acceder al historial de pruebas e identificar cualquier error relacionado con estas.
Si dispone de un caso de pruebas, puede consultar cualquier ejecución de pruebas que lo incluyan.

Con la ejecución de casos de pruebas puede consultar una operación de pruebas.

Con una ejecución de caso de pruebas fallida siempre puede consultar la lista de errores asociados.

Para aprender más sobre los flujos de trabajo descritos en este artículo o para debatir otros temas relacionados con los flujos de trabajo, no dude en unirse a nuestra comunidad YouTrack Workflow Community en Slack.

Discover more