唯一需求ID和相应测试方法列表

唯一需求ID和相应测试方法列表

我学会了如何创建具有唯一 ID 的需求在这篇文章中。现在我想为每个需求添加一个测试方法——在代码中我正在考虑这样的事情:

\Requirement{SOMEID}{This is the requirement text.}{This describes the testmethod.}

稍后,我想在另一个部分中迭代这些测试方法并列出它们。最后,我正在寻找类似这样的内容:

在此处输入图片描述

第一部分对我来说很清楚,但是如何“存储”一个参数(测试方法)以便以后使用它?

答案1

您需要一些“联想记忆”,它可以通过\csname或已经提供的数据结构来实现expl3

这里\Requirement将 ID 存储在一个序列中以便对其进行索引,然后将两个文本存储在属性列表中。存储需求文本在这里实际上并没有用到,但您可以决定以不同的方式使用它,因此最好也将其存储起来。

当需要时,可以映射序列以提供定义时存储的所有测试方法。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\Requirement}{mmm}
 {
  \subsubsection*{Requirement ~ ID ~ #1}
  #2
  \jfb_requirement_store:nnn { #1 } { #2 } { #3 }
 }
\NewDocumentCommand{\DescribeRequirements}{}
 {
  \seq_map_inline:Nn \g_jfb_requirement_ids_seq
   {
    \subsubsection*{##1} \prop_item:Nn \g_jfb_requirement_ids_prop { ##1@B } \par
   }
 }

\seq_new:N \g_jfb_requirement_ids_seq
\prop_new:N \g_jfb_requirement_ids_prop

\cs_new_protected:Nn \jfb_requirement_store:nnn
 {
  \seq_gput_right:Nn \g_jfb_requirement_ids_seq { #1 }
  \prop_gput:Nnx \g_jfb_requirement_ids_prop { #1@A } { \tl_trim_spaces:n { #2 } }
  \prop_gput:Nnx \g_jfb_requirement_ids_prop { #1@B } { \tl_trim_spaces:n { #3 } }
 }

\ExplSyntaxOff

\begin{document}

\section{Introduction}

Blah.

\section{Requirements}

\Requirement{SOMEID}{This is the requirement text.}{This describes the test method.}

\Requirement{SOMEOTHERID}{
  This is the other requirement text.
}{
  This describes the other test method.
}

\section{Test Methods}

\DescribeRequirements

\end{document}

在此处输入图片描述

答案2

您可以为此使用 LaTeX 的\@starttoc- -机制。\addtocontents

我尝试确保以下示例无论是否编译超链接正在加载。

我还尝试确保\label--\ref引用和书签能够正常工作。

您可能需要多次编译示例,而无需在 LaTeX 运行之间删除辅助文件以确保所有内容正确匹配。

\documentclass{article}

\usepackage{hyperref}

\newcounter{TestMethod}
\newcounter{Requirement}

\makeatletter
\newcommand\listofrequirements{%
  \section{Requirements}%
  \IfFileExists{\jobname.lfr}{}{\par\noindent No requirements available.}%
  \@starttoc{lfr}%
}%
\newcommand\listoftestmethods{%
  \section{Testmethods}%
  \begin{description}%
  \def\makelabel##1{\hspace\labelsep\normalfont\bfseries##1}%
  \IfFileExists{\jobname.ltm}{}{\item[\textnormal{No test-methods available.}]\relax}%
  \@starttoc{ltm}%
  \end{description}%
}%
\newcommand\@multipleRequirements{}%
\AtEndDocument{\@multipleRequirements}%
\begingroup
\catcode`\/=14 %
\catcode`\%=12 /
\@firstofone{/
\endgroup
\newcommand\Requirement[3]{/
  \@bsphack
  {/
    \@ifundefined{Requirement@#1}{\global\@namedef{Requirement@#1}{DEFINED}}{/
      \gdef\@multipleRequirements{\@latex@warning@no@line{There were multiply-defined requirements}}/
      \@latex@warning{Requirement `#1' multiply defined}/
    }/
  }/
  \begingroup
  // Let's have \addtocontents / \protected@write write immediately:
  \let\writecopy\write
  \def\write{\immediate\writecopy}/
  // Prevent all of \protected@write's expansion (\edef-with \protect-mechanism-expansion and \write-expansion)
  // by nesting things either into two token-registers or into two \unexpanded.
  \toks@{\the\@temptokena}/
  \@temptokena\expandafter{\string\l@Requirement{%^^J{#1}%^^J{#2}%^^J}}/
  \addtocontents{lfr}{\the\toks@\relax}/
  \@temptokena\expandafter{\string\l@TestMethod{%^^J{#1}%^^J{#3}%^^J}}/
  \addtocontents{ltm}{\the\toks@\relax}/
  /\addtocontents{lfr}{\unexpanded{\unexpanded\expandafter{\string\l@Requirement{%^^J{#1}%^^J{#2}%^^J}}}\relax}/
  /\addtocontents{ltm}{\unexpanded{\unexpanded\expandafter{\string\l@TestMethod{%^^J{#1}%^^J{#3}%^^J}}}\relax}/
  \endgroup
  \@esphack
}/
}%
\newcommand\l@Requirement[1]{%
  %#1 =  {ID}{<requirement-text>}
  \PrintRequirement#1%
}%
\newcommand\l@TestMethod[1]{%
  %#1 = {ID}{<requirement-text>}
  \PrintTestMethod#1%
}%
\newcommand\PrintRequirement[2]{%
  %#1 = ID; #2 = <requirement-text>;
  \ifhmode\medskip\fi\par\noindent
  \begingroup
  \def\theRequirement{#1}%
  \refstepcounter{Requirement}%
  \def\@currentlabelname{#1}%
  \label{RequirementNamespace@Requirement #1}%
  \@ifundefined{Hy@raisedlink}{}{%
    \@tempcnta\Hy@currentbookmarklevel
    \Hy@StepCount\@tempcnta
    \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
    {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
    \advance \@tempcnta by -1 %
    \xdef\Hy@currentbookmarklevel{\the \@tempcnta}%
    \@ifundefined{r@RequirementNamespace@TestMethod #1}{}{%
      \hyperref[{RequirementNamespace@TestMethod #1}]%
    }%
  }%
  {\textbf{Requirement~ID~{#1}}}%
  \\{#2}%
  \endgroup
}%
\newcommand\Requirement@MoveLabelData{}%
\newcommand\RequirementPassFirstToSecond[2]{#2{#1}}%
\newcommand\PrintTestMethod[2]{%
  %#1 = ID; #2 = <requirement-text>;
  \item[{%
    \def\theTestMethod{#1}%
    \refstepcounter{TestMethod}%
    \def\@currentlabelname{#1}%
    \expandafter\gdef\expandafter\Requirement@MoveLabelData\expandafter{%
      \romannumeral0%
      \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabel}{%
        \@ifundefined{Hy@raisedlink}{ }{%
          \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabelname}{%
            \expandafter\RequirementPassFirstToSecond\expandafter{\@currentHref}{ %<- This space must be!
              \def\@currentHref
            }%
            \def\@currentlabelname
          }%
        }%
        \def\@currentlabel
      }%
    }%
    \label{RequirementNamespace@TestMethod #1}%
    \@ifundefined{Hy@raisedlink}{}{%
      \@tempcnta\Hy@currentbookmarklevel
      \Hy@StepCount\@tempcnta
      \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
      {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
      \advance\@tempcnta by -1 %
      \xdef\Hy@currentbookmarklevel{\the \@tempcnta}%
      \@ifundefined{r@RequirementNamespace@Requirement #1}{}{%
        \hyperref[{RequirementNamespace@Requirement #1}]%
      }%
    }%
    {#1:}%
  }]%
  \Requirement@MoveLabelData
  #2%
}%
\makeatother

\begin{document}

\section{This is just some section}

This is text in the section.

\noindent Reference to the requirement with ID ``SOMEOTHERID'': \ref{AA}.

\noindent Reference to the testing-method with ID ``SOMEOTHERID'': \ref{BB}.

\Requirement{SOMEID}{This is the requirement text.}{This describes the testmethod.}
\Requirement{SOMEOTHERID}{This \label{AA} is some different requirement text.}{This \label{BB} describes some different testmethod.}

\listofrequirements

\listoftestmethods

\end{document}

如果辅助文件中的行数过长,您可以按照逐字分类代码制度读取和写入一些参数。如果这样做,则宏\Requirement不能在其他宏的定义和/或参数中使用。

一开始我无法让它工作解析v+-argument-type,所以我使用了我自己的东西。

我无法让它工作解析v+-argument-type,原因如下:解析v+-argument-type 使用^^M-character = ⟨return⟩ 字符来表示行尾。(^^M=^^表示 TeX 引擎内部字符编码方案中代码点为 13 的字符 -M 是字母表中的第 13 个字母;TeX 引擎内部字符编码方案的代码点 13 反过来表示 ⟨return⟩ 字符。)因此,在将 -argument 写入v+文件时,整数参数\newlinechar也必须表示该字符。因此,您最好不要使用将-argument\protected@write写入v+文件,而是使用立即执行的操作\write,而更改\newlinechar仍然有效。在我第一次尝试使用解析v+-argument-type 我忽略了必须\write立即发生。

这是我自己的东西,但在那后面我添加了另一个例子,使用解析及其v+用于获取逐字参数的 -argument-type:

\documentclass{article}

\usepackage{hyperref}

\newcounter{TestMethod}
\newcounter{Requirement}

\makeatletter

%%======================Code for \UDcollectverbarg=============================
%% \UDcollectverbarg{^^M-replacement}{<mandatory 1>}{<mandatory 2>}|<verbatim arg>|
%% 
%% reads <verbatim arg> under verbatim-catcode-regime and delivers:
%%
%%    <mandatory 1>{<mandatory 2>{<verbatim arg>}{|<verbatim arg>|}}
%%
%% Instead of verbatim-delimiter | the <verbatim arg> can be nested in braces.
%% You cannot use percent or spaces or horizontal tab as verbatim-delimiter.
%%
%% You can use <mandatory 1> for nesting calls to \UDcollectverbarg.
%% <mandatory 2> gets the <verbatim arg> twice: Once without verbatim-delimiters/braces,
%% once surrounded by verbatim-delimiters/braces.
%% Reason: When you feed it to \scantokens you don't need the verbatim-delimiters.
%%         When you use it for writing to temporary files and reading back,
%%         you need them.
%%=============================================================================
%% Check whether argument is empty:
%%=============================================================================
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%    
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" by \expandafter.
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
\long\def\UD@CheckWhetherNull#1{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%=============================================================================
\begingroup
\@makeother\^^M%
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \@ifdefinable\@UDEndlreplace{%
    \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
      \UD@CheckWhetherNull{#3}%
      { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
    }%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \catcode`\%=14     % <- make percent comment.
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \do\%%   <- Give the percent-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    \def\@tempb{##1}%
    \UD@CheckWhetherNull{#4}{%
      \def\@tempc{{##1}}%
    }{%
      \def\@tempc{#4##1#4}%
    }%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts 
                               %    the loop for replacing endline-characters.
    \@onelevel@sanitize\@tempc
    \expandafter\UDEndlreplace\expandafter{\@tempc}{#1}{\def\@tempc}%
    \expandafter\expandafter\expandafter\UD@@collectverbarg% <- this "spits out the result.
    \expandafter\expandafter\expandafter{%
    \expandafter\@tempb\expandafter}%
    \expandafter{\@tempc}{#2}{#3}%
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[4]{%
  \endgroup
  #3{#4{#1}{#2}}%
}%
%%================= End of code for \UDcollectverbarg =========================

\newcommand\listofrequirements{%
  \section{Requirements}%
  \IfFileExists{\jobname.lfr}{}{\par\noindent No requirements available.}%
  \@starttoc{lfr}%
}%
\newcommand\listoftestmethods{%
  \section{Testmethods}%
  \begin{description}%
  \def\makelabel##1{\hspace\labelsep\normalfont\bfseries##1}%
  \IfFileExists{\jobname.ltm}{}{\item[\textnormal{No test-methods available.}]\relax}%
  \@starttoc{ltm}%
  \end{description}%
}%
\newcommand\@multipleRequirements{}%
\AtEndDocument{\@multipleRequirements}%
\newcommand\Requirement[1]{%
   \@bsphack
   \UDcollectverbarg{^^J}{%
     \UDcollectverbarg{^^J}{\@firstofone}%
   }{\InnerRequirement{#1}}%
}%
\newcommand\InnerRequirement[5]{%
  {%
    \@ifundefined{Requirement@#1}{\global\@namedef{Requirement@#1}{DEFINED}}{%
      \gdef\@multipleRequirements{\@latex@warning@no@line{There were multiply-defined requirements}}%
      \@latex@warning{Requirement `#1' multiply defined}%
    }%
  }%
  \begingroup
  \immediate\write\@auxout{\string\Requirementwritefile{lfr}{Requirement}{#1}#3}%
  \immediate\write\@auxout{\string\Requirementwritefile{ltm}{TestMethod}{#1}#5}%
  \endgroup
  \@esphack
}%
\newcommand\Requirementwritefile[3]{%
  \UDcollectverbarg{^^J}{\@firstofone}{%
    \expandafter\expandafter\expandafter\RequirementPassFirstToSecond
    \expandafter\expandafter\expandafter{\expandafter\string\csname l@#2\endcsname}%
    {\RequirementwritefileInner{#1}}{#3}%
  }%
}%
\begingroup
\catcode`\/=14 %
\@makeother{\%}/
\@firstofone{/
  \endgroup
  \newcommand\RequirementwritefileInner[5]{/
    \@writefile{#1}{/
      #2{%^^J/
      {#3}%^^J/
      {#4}%^^J/
      }%/
    }/
  }/
}%
\newcommand\l@Requirement[1]{%
  %#1 =  {ID}{<requirement-text>}
  \PrintRequirement#1%
}%
\newcommand\l@TestMethod[1]{%
  %#1 = {ID}{<requirement-text>}
  \PrintTestMethod#1%
}%
\newcommand\PrintRequirement[2]{%
  %#1 = ID; #2 = <requirement-text>;
  \ifhmode\medskip\fi\par\noindent
  \begingroup
  \def\theRequirement{#1}%
  \refstepcounter{Requirement}%
  \def\@currentlabelname{#1}%
  \label{RequirementNamespace@Requirement #1}%
  \@ifundefined{Hy@raisedlink}{}{%
    \@tempcnta\Hy@currentbookmarklevel
    \Hy@StepCount\@tempcnta
    \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
    {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
    \advance\@tempcnta by -1 %
    \xdef\Hy@currentbookmarklevel{\the\@tempcnta}%
    \@ifundefined{r@RequirementNamespace@TestMethod #1}{}{%
      \hyperref[{RequirementNamespace@TestMethod #1}]%
    }%
  }%
  {\textbf{Requirement~ID~{#1}}}%
  \\{#2}%
  \endgroup
}%
\newcommand\Requirement@MoveLabelData{}%
\newcommand\RequirementPassFirstToSecond[2]{#2{#1}}%
\newcommand\PrintTestMethod[2]{%
  %#1 = ID; #2 = <requirement-text>;
  \item[{%
    \def\theTestMethod{#1}%
    \refstepcounter{TestMethod}%
    \def\@currentlabelname{#1}%
    \expandafter\gdef\expandafter\Requirement@MoveLabelData\expandafter{%
      \romannumeral0%
      \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabel}{%
        \@ifundefined{Hy@raisedlink}{ }{%
          \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabelname}{%
            \expandafter\RequirementPassFirstToSecond\expandafter{\@currentHref}{ %<- This space must be!
              \def\@currentHref
            }%
            \def\@currentlabelname
          }%
        }%
        \def\@currentlabel
      }%
    }%
    \label{RequirementNamespace@TestMethod #1}%
    \@ifundefined{Hy@raisedlink}{}{%
      \@tempcnta\Hy@currentbookmarklevel
      \Hy@StepCount\@tempcnta
      \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
      {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
      \advance\@tempcnta by -1 %
      \xdef\Hy@currentbookmarklevel{\the\@tempcnta}%
      \@ifundefined{r@RequirementNamespace@Requirement #1}{}{%
        \hyperref[{RequirementNamespace@Requirement #1}]%
      }%
    }%
    {#1:}%
  }]%
  \Requirement@MoveLabelData
  #2%
}%
\makeatother

\begin{document}

\section{This is just some section}

This is text in the section.

\noindent Reference to the requirement with ID ``SOMEOTHERID'': \ref{AA}.

\noindent Reference to the testing-method with ID ``SOMEOTHERID'': \ref{BB}.

\Requirement{SOMEID}{This is the requirement text.}{This describes the testmethod.}
\Requirement{SOMEOTHERID}{This \label{AA} is some different requirement text.}{This \label{BB} describes some different testmethod.}

\listofrequirements

\listoftestmethods

\end{document}

下面是使用逐字参数的示例,使用解析+v参数类型:

\documentclass{article}

\usepackage{xparse}
\usepackage{hyperref}

\newcounter{TestMethod}
\newcounter{Requirement}

\makeatletter
\NewDocumentCommand{\listofrequirements}{}{%
  \section{Requirements}%
  \IfFileExists{\jobname.lfr}{}{\par\noindent No requirements available.}%
  \@starttoc{lfr}%
}%
\NewDocumentCommand{\listoftestmethods}{}{%
  \section{Testmethods}%
  \begin{description}%
  \def\makelabel##1{\hspace\labelsep\normalfont\bfseries##1}%
  \IfFileExists{\jobname.ltm}{}{\item[\textnormal{No test-methods available.}]\relax}%
  \@starttoc{ltm}%
  \end{description}%
}%
\NewDocumentCommand{\@multipleRequirements}{}{}%
\AtEndDocument{\@multipleRequirements}%
\NewDocumentCommand{\Requirement}{m}{%
   \begingroup
   \@makeother{\^^I}%
   \InnerRequirement{#1}%
}%
\begingroup
\catcode`\/=14 %
\@makeother{\%}/
\@makeother{\^^M}/
\@firstofone{/
\endgroup/
\NewDocumentCommand{\InnerRequirement}{m+v+v}{/
  \endgroup/
  {/
    \@ifundefined{Requirement@#1}{\global\@namedef{Requirement@#1}{DEFINED}}{/
      \gdef\@multipleRequirements{\@latex@warning@no@line{There were multiply-defined requirements}}/
      \@latex@warning{Requirement `#1' multiply defined}/
    }/
  }/
  \begingroup/
  \newlinechar=\endlinechar/
  \immediate\write\@auxout{/
    \string\Requirementwritefile{lfr}/
                                {\unexpanded\expandafter{\string\l@Requirement{%^^M{#1}%^^M{#2}%^^M}}%}/
  }/
  \immediate\write\@auxout{/
    \string\Requirementwritefile{ltm}/
                                {\unexpanded\expandafter{\string\l@TestMethod{%^^M{#1}%^^M{#3}%^^M}}%}/
  }/
  \endgroup/
  \@esphack/
}/
}%
\NewDocumentCommand{\Requirementwritefile}{m}{%
  \begingroup
  \@makeother{\^^I}%
  \RequirementwritefileInner{#1}%
}%
\NewDocumentCommand{\RequirementwritefileInner}{m+v}{%
  \newlinechar=\endlinechar
  \@writefile{#1}{#2}%
  \endgroup
}%
\NewDocumentCommand{\l@Requirement}{+m}{%
  %#1 =  {ID}{<requirement-text>}
  \PrintRequirement#1%
}%
\NewDocumentCommand{\l@TestMethod}{+m}{%
  %#1 = {ID}{<requirement-text>}
  \PrintTestMethod#1%
}%
\NewDocumentCommand{\PrintRequirement}{+m+m}{%
  %#1 = ID; #2 = <requirement-text>;
  \ifhmode\medskip\fi\par\noindent
  \begingroup
  \def\theRequirement{#1}%
  \refstepcounter{Requirement}%
  \def\@currentlabelname{#1}%
  \label{RequirementNamespace@Requirement #1}%
  \@ifundefined{Hy@raisedlink}{}{%
    \@tempcnta\Hy@currentbookmarklevel
    \Hy@StepCount\@tempcnta
    \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
    {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
    \advance\@tempcnta by -1 %
    \xdef\Hy@currentbookmarklevel{\the \@tempcnta}%
    \@ifundefined{r@RequirementNamespace@TestMethod #1}{}{%
      \hyperref[{RequirementNamespace@TestMethod #1}]%
    }%
  }%
  {\textbf{Requirement~ID~{#1}}}%
  \\{#2}%
  \endgroup
}%
\NewDocumentCommand{\Requirement@MoveLabelData}{}{}%
\NewDocumentCommand{\RequirementPassFirstToSecond}{+m+m}{#2{#1}}%
\NewDocumentCommand{\PrintTestMethod}{+m+m}{%
  %#1 = ID; #2 = <requirement-text>;
  \item[{%
    \def\theTestMethod{#1}%
    \refstepcounter{TestMethod}%
    \def\@currentlabelname{#1}%
    \expandafter\gdef\expandafter\Requirement@MoveLabelData\expandafter{%
      \romannumeral0%
      \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabel}{%
        \@ifundefined{Hy@raisedlink}{ }{%
          \expandafter\RequirementPassFirstToSecond\expandafter{\@currentlabelname}{%
            \expandafter\RequirementPassFirstToSecond\expandafter{\@currentHref}{ %<- This space must be!
              \def\@currentHref
            }%
            \def\@currentlabelname
          }%
        }%
        \def\@currentlabel
      }%
    }%
    \label{RequirementNamespace@TestMethod #1}%
    \@ifundefined{Hy@raisedlink}{}{%
      \@tempcnta\Hy@currentbookmarklevel
      \Hy@StepCount\@tempcnta
      \expandafter\RequirementPassFirstToSecond\expandafter{\the\@tempcnta}%
      {\Hy@writebookmark{}{#1}{\@currentHref}}{toc}%
      \advance \@tempcnta by -1 %
      \xdef\Hy@currentbookmarklevel{\the \@tempcnta}%
      \@ifundefined{r@RequirementNamespace@Requirement #1}{}{%
        \hyperref[{RequirementNamespace@Requirement #1}]%
      }%
    }%
    {#1:}%
  }]%
  \Requirement@MoveLabelData
  #2%
}%
\makeatother

\begin{document}

\section{This is just some section}

This is text in the section.

\noindent Reference to the requirement with ID ``SOMEOTHERID'': \ref{AA}.

\noindent Reference to the testing-method with ID ``SOMEOTHERID'': \ref{BB}.

\Requirement{SOMEID}{This is the requirement text.}{This describes the testmethod.}
\Requirement{SOMEOTHERID}{This \label{AA} is some different requirement text.}{This \label{BB} describes some different testmethod.}

\listofrequirements

\listoftestmethods

\end{document}

在此处输入图片描述

相关内容