Best Practices Features

Make It Workflow — Parte 13: Suporte a cenários de gerenciamento de testes

Read this post in other languages:

Bem-vindo de volta à nossa série Make It Workflow! Recebemos várias solicitações da comunidade para discutir os recursos do YouTrack que podem ser utilizados pela equipe de controle de qualidade. Hoje, gostaríamos de direcionar nossa atenção a como manter cenários de gerenciamento de testes (TMS) no YouTrack. Este artigo demonstrará como o YouTrack pode ser configurado para que você possa manter a funcionalidade de gerenciamento de testes dentro do próprio YouTrack, evitando assim o uso de soluções de terceiros. Essa abordagem ajuda a economizar em custos de licença e torna o fluxo do processo de gerenciamento de testes mais uniforme.

Este artigo é para gerentes de teste e qualquer pessoa interessada em workflows do YouTrack. Você encontrará um exemplo prático de como criar um processo de teste no YouTrack, com recomendações para configurações de projetos e issues, uma descrição de como implementar cenários de gerenciamento de testes e muito mais.

Também selecionamos uma lista de blocos de código de workflow prontos para uso que podem ser aplicados para automatizar os processos de gerenciamento de testes. Esses blocos de código tornam mais fácil associar casos de teste a uma execução de teste específica, clonar essa execução de teste, exibir sugestões do sistema no próximo teste e muito mais.

Configurações para projetos de gerenciamento de testes no YouTrack

Talvez você queira usar tipos de issues específicos de gerenciamento de testes para seus projetos de gerenciamento de testes, como Caso de Teste, Conjunto de Testes (um conjunto de casos de teste), Execução de Teste (um conjunto de casos de teste ou conjuntos de testes atribuídos a um ciclo de teste específico) e Execuções de Casos de Teste atribuídas a uma execução de teste específica. Você deve configurar os tipos de issues necessários para seu projeto de TMS. Existem 3 tipos de links de issues para estabelecer conexões e relevância entre suas tarefas de gerenciamento de testes:

  • “Parent-subtasks” padrão mantêm passagens de testes e execuções de casos de teste, bem como o conjunto de testes e relações de casos de teste.
  • “Execution” personalizada mantém relações de casos de teste e de execuções de casos de teste.
  • “Related Bug” personalizado mantém as relações entre os testes que falharam e os bugs atribuídos.

Os requisitos de testes podem ser mantidos como um artigo vinculado aos issues em um campo de texto.
Além disso, para as suas tarefas de gerenciamento de testes, você pode querer configurar campos personalizados, como Test mode, Category, Test flow e Application, juntamente com valores predefinidos (por exemplo, status de teste predefinidos). Você também pode usar campos condicionais quando precisa exibir informações especificamente para um tipo de issue.

Um conjunto de campos predefinidos para os tipos de problemas “Test Case” e “Test Run” pode ser ajustado de acordo com as necessidades da sua empresa.

Configurar passagens de testes e execução de testes

Quando, por exemplo, queremos testar uma versão de produto específica, precisamos criar uma passagem de teste e atribuir uma série de conjuntos de teste e casos de teste relevantes a ela. Aqui estão as etapas para fazer isso:

  • Crie um issue com o tipo de issue “Test Run”.
  • Use the ‘Assigned test case and test suite’ custom link type to link to the issue.
  • Na janela pop-up, especifique os casos de teste que você gostaria de incluir na passagem de teste.

As etapas do workflow “Populate Test Run” para preencher uma passagem de teste economizarão tempo e esforços da sua equipe de controle de qualidade:

  • Cópias de todos os conjuntos de teste e casos de teste selecionados serão criadas no sistema. Todos os issues terão o tipo de issue “Test Case Execution”.
  • O tipo de link “Execution” conectará as execuções dos casos de teste aos casos de teste selecionados.
  • Issues recém-criados serão vinculados à passagem de teste como um issue pai e suas subtarefas relacionadas.

É assim que funciona:

Expanda o bloco 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'
      },
    },
  }
});

Manutenção de atividades de teste

Depois que as passagens de testes estiverem totalmente configuradas, um engenheiro de teste poderá iniciar os testes. Por padrão, todos os issues com os tipos “Test Case Execution” e “Test Run” têm o status “No Run”.

Como alternar entre testes

Quando você está trabalhando com um conjunto de testes, existem duas maneiras de alternar de um teste para outro:

  1. Altere manualmente o status do teste na página da lista de issues.
  2. Abra um teste e mude para o próximo teste não concluído. 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:

    • Verifique se há testes com o status “No Run’ e pertencem à mesma passagem de teste.
    • Exiba uma mensagem com a URL do próximo teste disponível (se houver).

      ApplyingCommandForSubsystem

    Isso pode ser implementado usando o código abaixo.

    Expanda o bloco 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);
        }
    }
    }
    

Status de testes

Ao alternar de um teste para outro, um engenheiro de teste pode indicar que o teste foi aprovado (“Passed”) ou alterar o status do teste para reprovado (“Failed”) (caso um bug tenha sido identificado durante a execução do caso de teste). Com base nas configurações do YouTrack que inicialmente configuramos para esse projeto, existem vários status de passagem de teste predefinidos:

  • Failing: pelo menos um dos testes associados tem o status “No run” e pelo menos um dos testes associados tem o status “Failed”.
  • Passing: pelo menos um dos testes associados tem o status “No run” e não há testes associados com o status “Failed”.
  • Failed: nenhum teste associado tem o status “No run” e pelo menos um dos testes associados tem o status “Failed”.
  • Passed: nenhum teste associado tem o status “No Run” ou “Failed”.

Os status listados acima podem ajudar sua equipe de controle de qualidade a monitorar o progresso dos testes durante todo o ciclo de testes. É importante observar que o status da passagem de teste depende apenas dos testes associados e não deve ser alterado manualmente. O processo para determinar o status da passagem de teste é implementado com o uso do workflow state-machines per issue type. O bloco de código de opções de testes também está incorporado no workflow.

Expanda o bloco 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'
    },
  }
});
 

Estatísticas de testes

Você também pode estar interessado nas principais estatísticas do ciclo de teste. Para efeitos desta demonstração, incorporamos as seguintes métricas:

  • Número total de testes atribuídos a uma passagem de teste específica.
  • Número de testes com o status “Passed”.
  • Número de testes com o status “Failed”.


Graças ao código abaixo, todas as métricas são atualizadas no caso de qualquer uma das seguintes alterações: alterações de status do teste, a atribuição de um novo teste à passagem de teste e a remoção de um teste da passagem de teste.

Expanda o bloco 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;
};

No nosso caso, esse código é acionado em várias regras de workflow, como “Update stats when links are adjusted” e “Switch to the next test case”, entre outras. Se quiser adotar um conjunto de métricas que se adaptem às suas necessidades específicas, você poderá adicionar os campos personalizados necessários e ajustar sua lógica de workflow.

Recursos do rastreador de issues

Talvez você queira criar uma tarefa separada que resolva um bug encontrado para um teste com falha e vinculá-la a um issue que identifica a execução do caso de teste relacionada. Você pode vincular os testes que falharam aos bugs relacionados usando um tipo de link de problema personalizado ou usar um campo de texto personalizado contendo uma referência ao bug. Pode ser mais fácil organizar um projeto do YouTrack separado para manter o controle dos bugs.

Possíveis melhorias

Você pode estender a funcionalidade do YouTrack para atender a requisitos comerciais adicionais. Aqui estão várias opções a serem consideradas.

Clonagem de uma passagem de teste existente

Às vezes, pode ser muito útil fazer uma nova passagem de teste semelhante a uma existente, mas que inclua algumas pequenas alterações (por exemplo, uma mudança na versão atribuída). O item de menu “Create Test Run copy” é criado precisamente para esses casos. Esse item de menu está disponível apenas para issues do tipo “Test Run” e aciona as seguintes ações:

  • Cria uma cópia da passagem de teste, bem como cópias das execuções de caso de teste atribuídas a ela.
  • All newly created test case execution copies are assigned to the new test run using the ‘parent-child’ issue link type. Além disso, essas cópias serão atribuídas aos casos de teste originais com o tipo de link de issue “Execution” (para manter a rastreabilidade).
  • All statuses and statistics will be discarded for the newly created issue.

A lógica acima é implementada usando a regra de ação “Create Test Run copy”.

Expanda o bloco 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 as ações do usuário

Para evitar possíveis erros ao trabalhar com cenários de teste (por exemplo, usando os tipos de issues incorretos), você pode restringir as possíveis ações dos usuários. Por exemplo, você pode implementar ações para garantir que os usuários vinculem casos de teste a passagens de testes. 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.’ Se não for nenhum dos dois, a ação enviará um aviso que impedirá o usuário de prosseguir.

O seguinte bloco de código pode ser adicionado ao workflow “Populate Test Run”.

Expanda o bloco 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));
    

Conforme mencionado acima, o status da passagem de teste deve ser somente leitura, pois depende do andamento das execuções dos testes. Isso pode ser feito de maneira direta, simplesmente incluindo um bloco de código que restrinja as mudanças manuais de status para issues com o tipo de problema “Test Run”.


Por exemplo, a seção ‘transições’ para o tipo de issue ‘Test Run’ no workflow “status-management” pode ser ajustado da seguinte maneira:

Expanda o bloco 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'));
            }
          },
          ...
        }

Relatórios e rastreabilidade

Relatórios

Ao final do dia, após o término do ciclo de teste, talvez você queira analisar os resultados. O recurso de relatórios do YouTrack pode ser usado para essa finalidade. You can select the report type, which will display key information related to the test management procedure. Para efeitos dessa demonstração, adicionaremos 2 relatórios ao dashboard:

  • Relatório de fluxo cumulativo com filtragem pelo campo “version”. Esse tipo de relatório de status se refere a uma versão específica do produto. Ele mostra as principais métricas, como o número de testes com os status “Passed”, “Failed” e “No Run”, mapeados em uma linha do tempo. Para exibir dados de versão cruzada, você deve criar um relatório para cada versão. Pode ser conveniente adicionar todos os relatórios ao painel para que você possa trabalhar com todos os dados em um só lugar.

    Configuração do relatório:

    ApplyingCommandForSubsystem

    Resultados do relatório:

    ApplyingCommandForSubsystem

  • Relatório de distribuição de issues. Esse relatório é um snapshot que inclui as principais métricas (como o número de passagens de testes com cada status) para várias versões do produto. Especificamente para essa demonstração, incluímos resultados de relatórios que podem ajudá-lo a comparar versões e analisar a estabilidade da versão.

    Configuração do relatório:

    ApplyingCommandForSubsystem

    Resultados do relatório:

    ApplyingCommandForSubsystem

Rastreabilidade

Como o YouTrack armazena as principais informações relacionadas ao ciclo de teste para cada problema com o tipo “Test Execution”, você sempre pode acessar o histórico de teste e identificar bugs relacionados.
Se você tiver um caso de teste, poderá consultar qualquer execução de teste na qual ele esteja envolvido.

Com a execução de caso de teste, você pode consultar uma passagem de teste.

Com uma execução de caso de teste reprovada, você sempre pode consultar a lista de bugs associados.

Para encontrar mais detalhes sobre os workflows descritos neste artigo ou para discutir outros tópicos relacionados ao fluxo de trabalho, sinta-se à vontade para participar da nossa Comunidade de workflows do YouTrack no Slack.

image description

Discover more