Skip to main content

사용자 지정 쿼리 테스트

CodeQL 쿼리에 대한 테스트를 설정하여 CodeQL CLI의 새 릴리스로 예상 결과를 계속 반환하도록 할 수 있습니다.

누가 이 기능을 사용할 수 있나요?

CodeQL은(는) 다음 리포지토리 유형에 사용할 수 있습니다.

사용자 지정 쿼리 테스트 정보

CodeQL(은)는 쿼리의 자동화된 재발 테스트를 위한 간단한 테스트 프레임워크를 제공합니다. 쿼리를 테스트하여 예상대로 작동하는지 확인합니다.

쿼리 테스트 중에 CodeQL은(는) 사용자가 쿼리가 생성될 것으로 예상하는 결과를 실제로 생성된 결과와 비교합니다. 예상 결과와 실제 결과가 다르면 쿼리 테스트가 실패합니다. 테스트를 수정하려면 실제 결과와 예상 결과가 정확히 일치할 때까지 쿼리 및 예상 결과를 반복해야 합니다. 이 항목에서는 test run 하위 명령을 사용하여 테스트 파일을 만들고 테스트를 실행하는 방법을 보여 줍니다.

사용자 지정 쿼리에 대한 테스트 CodeQL 팩 설정

모든 CodeQL 테스트는 특수한 "테스트" CodeQL 팩에 저장해야 합니다. 즉, 다음을 정의하는 qlpack.yml 파일이 있는 테스트 파일의 디렉터리입니다.

name: <name-of-test-pack>
version: 0.0.0
dependencies:
  <codeql-libraries-and-queries-to-test>: "*"
extractor: <language-of-code-to-test>

dependencies 값은 테스트할 쿼리가 포함된 CodeQL 팩을 지정합니다. 일반적으로 이러한 팩은 원본에서 확인되므로 고정된 버전의 팩을 지정할 필요가 없습니다. 이 extractor(은)는 CodeQL 팩에 저장된 코드 파일에서 테스트 데이터베이스를 만드는 데 CLI에서 사용할 언어를 정의합니다. 자세한 내용은 "CodeQL 팩을 사용하여 분석 사용자 지정"을(를) 참조하세요.

쿼리 테스트가 CodeQL 리포지토리에서 구성되는 방식을 살펴보는 것이 유용할 수 있습니다. 각 언어에는 코드베이스를 분석하기 위한 라이브러리 및 쿼리를 포함하는 src 디렉터리 및 ql/<language>/ql/src(이)가 있습니다. src 디렉터리와 함께 이러한 라이브러리 및 쿼리에 대한 테스트가 포함된 test 디렉터리가 있습니다.

test 디렉터리는 두 개의 하위 디렉터리가 있는 테스트 CodeQL 팩으로 구성됩니다.

  • query-tests src 디렉터리에 저장된 쿼리에 대한 테스트가 포함된 일련의 하위 디렉터리입니다. 각 하위 디렉터리에는 테스트 코드와 테스트할 쿼리를 지정하는 QL 참조 파일이 포함됩니다.
  • library-tests QL 라이브러리 파일에 대한 테스트를 포함하는 일련의 하위 디렉터리입니다. 각 하위 디렉터리에는 라이브러리용 단위 테스트로 작성된 테스트 코드 및 쿼리가 포함됩니다.

qlpack.yml 파일을 만든 후에는 모든 종속성이 다운로드되어 CLI에서 사용할 수 있는지 확인해야 합니다. qlpack.yml 파일과 동일한 디렉터리에서 다음 명령을 실행하여 이 작업을 수행합니다.

codeql pack install

이렇게 하면 이 팩에서 쿼리를 실행하는 데 필요한 모든 전이적 종속성을 지정하는 codeql-pack.lock.yml 파일을 생성하게 됩니다. 이 파일은 소스 관리에 체크인되어야 합니다.

쿼리용 테스트 파일 설정

테스트하려는 각 쿼리용으로 테스트 CodeQL 팩에 하위 디렉터리를 만들어야 합니다. 그런 다음 테스트 명령을 실행하기 전에 하위 디렉터리에 다음 파일을 추가합니다.

  • 테스트할 쿼리의 위치를 정의하는 쿼리 참조 파일(.qlref 파일)입니다. 위치는 쿼리가 포함된 CodeQL 팩의 루트를 기준으로 정의됩니다. 일반적으로 테스트 팩의 dependencies 블록에 지정된 CodeQL 팩입니다. 자세한 내용은 "쿼리 참조 파일"을(를) 참조하세요.

    테스트하려는 쿼리가 테스트 디렉터리에 저장된 경우 쿼리 참조 파일을 추가할 필요가 없지만, 일반적으로 테스트와 별도로 쿼리를 저장하는 것이 좋습니다. 유일한 예외는 경고 또는 경로를 생성하는 쿼리와는 별도로 테스트 팩에 저장되는 경향이 있는 QL 라이브러리용 단위 테스트입니다.

  • 쿼리를 실행할 예제 코드입니다. 쿼리가 식별하도록 설계된 코드의 예제를 포함하는 하나 이상의 파일로 구성되어야 합니다.

확장명이 .expected인 파일을 만들어 예제 코드용 쿼리를 실행할 때 예상되는 결과를 정의할 수도 있습니다. 테스트 명령을 그대로 두고 .expected 파일을 만들 수도 있습니다.

쿼리를 만들고 테스트하는 방법을 보여 주는 예제는, 아래 예제를 참조하세요.

참조: 사용자 .ql, .qlref.expected 파일은 일관된 이름이 있어야 합니다.

  • 테스트 명령에서 .ql 파일 자체를 직접 지정하려면 해당 .expected 파일과 동일한 기본 이름이 있어야 합니다. 예를 들어 쿼리가 MyJavaQuery.ql인 경우 예상 결과 파일은 MyJavaQuery.expected(이)가 되어야 합니다.

  • 테스트 명령에서 .qlref 파일을 지정하려면, 해당 .expected 파일과 동일한 기본 이름이 있어야 하지만 쿼리 자체 이름은 다를 수도 있습니다.

  • 예제 코드 파일의 이름은 다른 테스트 파일과 일치할 필요가 없습니다. .qlref (또는.ql) 파일 옆에 있는 모든 예제 코드 파일과 하위 디렉터리에서 테스트 데이터베이스를 만드는 데 사용됩니다. 따라서 간단히 하기 위해 서로의 상위 항목인 디렉터리에 테스트 파일을 저장하지 않는 것이 좋습니다.

codeql test run 실행

CodeQL 쿼리 테스트는 다음 명령을 실행하여 실시됩니다.

codeql test run <test|dir>

<test|dir> 인수는 다음 중 하나 이상이 될 수 있습니다.

  • .ql 파일에 대한 경로입니다.
  • .ql 파일을 참조하는 .qlref 파일의 경로입니다.
  • .ql.qlref 파일을 재귀적으로 검색할 디렉터리 경로입니다.

다음을 지정할 수도 있습니다.

  • --threads: 선택적으로 쿼리를 실행할 때 사용할 스레드 수입니다. 기본 옵션은 1입니다. 더 많은 스레드를 지정하여 쿼리 실행 속도를 높일 수 있습니다. 스레드 수를 논리 프로세서 수와 일치시키려면 0(으)로 지정합니다.

쿼리를 테스트할 때 사용할 수 있는 모든 옵션에 대한 전체 내용은 "테스트 실행"(을)를 참조하세요.

예시

다음 예제에서는 Java 코드에서 빈 then 블록이 있는 if 문을 검색하는 쿼리에 대한 테스트를 설정하는 방법을 보여 줍니다. 여기에는 사용자 지정 쿼리 및 해당 테스트 파일을 추가하여 CodeQL 리포지토리의 체크 아웃 외부에 있는 CodeQL 팩을 구분하는 단계가 포함되어 있습니다. 이렇게 하면 CodeQL 라이브러리를 업데이트하거나 다른 분기를 체크 아웃할 경우 사용자 지정 쿼리 및 테스트를 덮어쓰지 않습니다.

쿼리 및 테스트 파일 준비

  1. 쿼리를 개발합니다. 예를 들어 다음의 간단한 쿼리는 Java 코드에서 빈 then 블록을 찾습니다.

    import java
    
    from IfStmt ifstmt
    where ifstmt.getThen() instanceof EmptyStmt
    select ifstmt, "This if statement has an empty then."
    
  2. 다른 사용자 지정 쿼리를 사용하여 디렉터리에 EmptyThen.ql(으)로 명명된 파일에 쿼리를 저장합니다. 예: custom-queries/java/queries/EmptyThen.ql.

  3. 사용자 지정 쿼리를 CodeQL 팩에 아직 추가하지 않은 경우 이제 CodeQL 팩을 만듭니다. 예를 들어 사용자 지정 Java 쿼리가 custom-queries/java/queries에 저장된 경우 다음 내용이 포함된 qlpack.yml 파일을 custom-queries/java/queries에 추가합니다.

    name: my-custom-queries
    dependencies:
      codeql/java-queries: "*"
    

    CodeQL에 대한 자세한 정보는 CodeQL 팩을 사용하여 분석 사용자 지정을(를) 참조하세요.

  4. 다음 내용이 포함된 qlpack.yml 파일을 custom-queries/java/tests에 추가하고 사용자 지정 쿼리의 CodeQL 팩의 이름과 일치하도록 dependencies(을)를 업데이트하여 Java 테스트용 CodeQL 팩을 만듭니다.

    다음 qlpack.yml 파일은 1.2.3보다 크거나 같은 버전과 2.0.0보다 작은 버전에 my-github-user/my-query-tests따라 달라집니다 my-github-user/my-custom-queries . 또한 CLI는 테스트 데이터베이스를 만들 때 Java extractor을(를) 사용해야 한다고 선언합니다. 이 tests: . 줄은 --strict-test-discovery 옵션을 사용하여 실행할 때 팩의 모든 .ql 파일을 테스트로 codeql test run실행해야 한다고 선언합니다. 일반적으로 테스트 팩에는 version 속성이 포함되지 않습니다. 이렇게 하면 실수로 게시되지 않습니다.

    name: my-github-user/my-query-tests
    dependencies:
      my-github-user/my-custom-queries: ^1.2.3
    extractor: java-kotlin
    tests: .
    
  5. 테스트 디렉터리의 루트에서 codeql pack install(을)를 실행합니다. 이렇게 하면 이 팩에서 쿼리를 실행하는 데 필요한 모든 전이적 종속성을 지정하는 codeql-pack.lock.yml 파일이 생성됩니다.

  6. Java 테스트 팩 내에서 EmptyThen.ql(와)과 연결된 테스트 파일을 포함할 디렉터리를 만듭니다. 예: custom-queries/java/tests/EmptyThen.

  7. 새 디렉터리에서 EmptyThen.qlref(을)를 만들어 EmptyThen.ql의 위치를 정의합니다. 디렉터리의 경로는 쿼리가 포함된 CodeQL 팩의 루트를 기준으로 지정되어야 합니다. 이 경우 쿼리는 my-query-tests에 대한 종속성으로 선언된 my-custom-queries이라는 CodeQL 팩의 최상위 디렉터리에 있습니다. 따라서 EmptyThen.qlref(은) 단순히 EmptyThen.ql(을)를 포함해야 합니다.

  8. 코드 조각을 만듭니다. 다음 Java 코드는 세 번째 줄에 빈 if 문을 포함합니다. custom-queries/java/tests/EmptyThen/Test.java에 저장합니다.

    class Test {
      public void problem(String arg) {
        if (arg.isEmpty())
          ;
        {
          System.out.println("Empty argument");
        }
      }
    
      public void good(String arg) {
        if (arg.isEmpty()) {
          System.out.println("Empty argument");
        }
      }
    }
    

테스트를 실행합니다.

테스트를 실행하려면 custom-queries 디렉터리로 이동하여 codeql test run java/tests/EmptyThen(을)를 실행합니다.

테스트가 실행되면 다음을 수행합니다.

  1. EmptyThen 디렉터리에서 테스트 하나를 찾습니다.

  2. EmptyThen 디렉터리에 저장된 .java 파일에서 CodeQL 데이터베이스를 추출합니다.

  3. EmptyThen.qlref 파일에서 참조하는 쿼리를 컴파일합니다.

    이 단계가 실패하면 CLI에서 사용자 지정 CodeQL 팩을 찾지 못하기 때문입니다. 명령을 다시 실행하고 사용자 지정 CodeQL 팩의 위치를 지정합니다. 예를 들면 다음과 같습니다.

    codeql test run --search-path=java java/tests/EmptyThen

    구성의 일부로 검색 경로를 저장하는 방법에 대한 자세한 내용은 "CodeQL 구성 파일에서 명령 옵션 지정하기"(을)를 참조하세요.

  4. 쿼리를 실행하고 EmptyThen.actual 결과 파일을 생성하여 테스트를 실행합니다.

  5. .actual 결과 파일과 비교할 EmptyThen.expected 파일을 확인합니다.

  6. 테스트 결과를 보고합니다. 0 tests passed; 1 tests failed:의 경우 오류가 발생합니다. 쿼리의 예상 결과가 포함된 파일을 아직 추가하지 않았기 때문에 테스트에 실패했습니다.

쿼리 테스트 출력 보기

CodeQL은(는) EmptyThen 디렉터리에 다음 파일을 생성합니다.

  • EmptyThen.actual, 쿼리에서 생성된 실제 결과를 포함하는 파일입니다.
  • EmptyThen.testproj, VS Code에 로드하고 실패한 테스트를 디버그하는 데 사용할 수 있는 테스트 데이터베이스입니다. 테스트가 성공적으로 완료되면 이 데이터베이스는 하우스키핑 단계에서 삭제됩니다. --keep-databases 옵션으로 test run(을)를 실행하여 이 단계를 재정의할 수 있습니다.

이 경우 예상된 오류를 쉽게 수정할 수 있습니다. EmptyThen.actual 파일을 열면 테스트 결과를 볼 수 있습니다.


| Test.java:3:5:3:22 | stmt | This if statement has an empty then. |

이 파일에는 쿼리가 출력하는 select 절의 각 부분에 대한 별도의 열과 함께 결과 위치에 대한 열이 있는 테이블이 포함되어 있습니다. 예상된 결과이므로 파일 확장자를 업데이트하여 이 테스트(EmptyThen.expected)에 대한 예상 결과로 정의할 수 있습니다.

지금 테스트를 다시 실행하면 출력은 비슷하지만 All 1 tests passed.(을)를 보고하여 완료됩니다.

예를 들어 쿼리의 결과가 변경되면 쿼리에 대한 select 문을 수정할 경우, 테스트가 실패합니다. 실패한 결과의 경우 CLI 출력에는 EmptyThen.expectedEmptyThen.actual 파일의 통합된 차이가 포함됩니다. 이 정보는 간단한 테스트 오류를 디버그하기에 충분할 수 있습니다.

디버그하기 어려운 오류의 경우, EmptyThen.testproj(을)를 VS Code용 CodeQL(으)로 가져와 Test.java예제 코드에서 EmptyThen.ql(을)를 실행하고 결과를 확인할 수 있습니다. 자세한 정보는 "CodeQL 데이터베이스 관리" 항목을 참조하세요.

추가 참고 자료