내가 당신에게 말하면, 당신은 당신의 단위 테스트를 실행할 필요가 없습니까?

13829 단어 cpp


소개



(아직) 나를 태우지 마십시오. 단위 테스트를 작성하지 말아야 한다는 말은 아닙니다. 중요합니다. 어떤 경우에는 직접 실행할 필요가 없다고 말하는 것입니다. 이유를 설명하겠습니다.

일화



몇 주 전에 2std::array를 하나로 연결하는 함수를 작성해야 했습니다. 함수는 매우 간단하고 다음과 같습니다.

#include <type_traits>
#include <array>
#include <algorithm>

template <typename T, std::size_t aSize, std::size_t bSize>
constexpr auto concat_array(const std::array<T, aSize>& a, const std::array<T, bSize>& b)
{
    std::array<T, aSize + bSize> result_array;
    std::ranges::copy(a, result_array.begin());
    std::ranges::copy(b, result_array.begin() + aSize);
    return result_array;
}


동일한 유형의 두 개의 std::array를 사용하고 크기가 다른 두 개의 std::array 크기와 같은 새 std::array를 만든 다음 첫 번째 항목의 내용을 복사한 다음 다음 내용을 복사합니다. 두 번째 것.

내 기능을 작성했습니다. 이제 테스트를 작성할 때입니다. 일부는 이전에 작성하고 tdd를 사용했어야 한다고 주장할 수 있지만 저는 그렇게 하지 않았으며 이것이 이 기사의 요점이 아닙니다. 이 기사의 단순함을 위해 첫 번째 테스트만 보여주고 다음과 같이 보일 것입니다.

// In reality I use Doctest instead of just some assert, but it is simpler to show it this way
int basic_test()
{
    const std::array<int, 3> a = { 1, 2, 3 };
    const std::array<int, 2> b = { 1, 2 };
    const auto res = concat_array(a, b);
    assert((res == std::array<int, 5>{ 1, 2, 3, 1, 2 }));
}


내 테스트 통과, 나는 행복하고 더 많은 테스트를 작성할 준비가 되었지만 갑자기 생각했습니다. 주장, 나는 만들 수 있습니다 constexpr ". 그래서, 나는 그렇게했다 :

constexpr std::array<int, 3> a = { 1, 2, 3 };
constexpr std::array<int, 2> b = { 1, 2 };
constexpr auto res = concat_array(a, b);
static_assert(res == std::array<int, 5>{ 1, 2, 3, 1, 2 });


그것은 오류 없이 컴파일되었고 내 테스트가 통과되었다는 것을 의미했으며 테스트를 실행할 필요조차 없었습니다. 컴파일러를 사용하기만 하면 테스트가 실행됩니다. 컴파일 중에 실행할 수 있는 모든 코드(constexpr 함수, static_assert 함수, 템플릿 항목 등)에 대해 작동합니다.

대회 가능한 한 많이 Constexpr



컴파일하는 동안 모든 코드를 실행할 수 있는 것은 아닙니다. 이제 C++ 20을 사용하면 많은 작업을 수행할 수 있습니다(C++23이 출시되면 더 많이). 내 말은, 예를 들어 을 사용하여 메모리를 할당할 수 있습니다. 예, constexpr 함수에서 std::vector 및 std::string을 사용할 수 있습니다. constexpr 가상 메소드를 가질 수도 있습니다.

ASCII 문자열에 포함된 모든 숫자를 추가하는 함수를 만들어 보겠습니다.

#include <vector>
#include <string_view>
#include <numeric>

// std::isdigit is not constexpr
constexpr bool is_digit(char c)
{
    return c >= '0' && c <= '9';
}

constexpr unsigned int accumulate_string_digits(std::string_view str)
{
    std::vector<unsigned int> digits;
    for (auto c: str)
    {
        if (is_digit(c))
            digits.push_back(c - 48);
    }
    return std::accumulate(digits.begin(), digits.end(), 0);
}


std::vector를 생성하지 않고도 이 작업을 쉽게 수행할 수 있었지만 내 예에는 적합하지 않습니다.

이제 테스트:

static_assert(accumulate_string_digits("") == 0);
static_assert(accumulate_string_digits("1") == 1);
static_assert(accumulate_string_digits("12345") == 15);
static_assert(accumulate_string_digits("1a23c45c") == 15);
static_assert(accumulate_string_digits("Hello, World!") == 0);


효과가있다 ! 불행히도 현재로서는 최신 버전의 msvc(Visual Studio와 함께 제공되는 Microsoft 컴파일러)에서만 Clang 및 Gcc가 constexpr std::vector를 아직 구현하지 않았습니다.

새로운 결론



내가 당신의 단위 테스트를 실행할 필요가 없다고 말했을 때 나는 약간 사실을 왜곡했습니다. 당신은 때때로 컴파일 시간 동안 컴파일러가 당신을 위해 그것을 실행하도록 할 수 있습니다. 또한 C++20(또는 향후 C++23)에서도 많은 코드가 constexpr이 될 수 없으므로 이것이 모든 코드에 적용되는 것은 아니지만 가능하면 강력한 도구입니다!

지금부터는 최소한 내 애완 동물 프로젝트에서 사용할 수 있는 최신 컴파일러가 있을 때 하는 것입니다. 컴파일 시간을 너무 많이 늘리지 않기 위해 내 클래식 런타임 단위 테스트와 함께 이 테스트를 다른 파일에 보관합니다.

출처


  • https://en.cppreference.com/w/cpp/container/array
  • https://en.cppreference.com/w/cpp/language/constexpr
  • https://en.cppreference.com/w/cpp/language/static_assert
  • https://en.cppreference.com/w/cpp/language/consteval
  • std::array code example on compiler explorer
  • accumulate_string_digits code example on compiler explorer
  • 좋은 웹페이지 즐겨찾기