문제

이것은 내가 아는 어렵고 개방적인 질문이지만, 바닥에 던지고 누군가 흥미로운 제안이 있는지 알아보고 싶다고 생각했습니다.

저는 Python 인터페이스를 C++ 코드(SWIG를 통해 생성됨)로 가져오고 이를 WebServices로 노출하는 데 필요한 코드를 생성하는 코드 생성기를 개발했습니다.이 코드를 개발할 때 TDD를 사용했지만 테스트가 매우 취약하다는 것을 알았습니다.각 테스트는 본질적으로 주어진 입력 코드(C++ 헤더)에 대해 주어진 출력 코드 비트를 얻을 수 있는지 확인하기를 원했기 때문에 XML 입력 파일에서 테스트 정의를 읽고 테스트를 생성하는 작은 엔진을 작성했습니다. 이러한 기대에서 나온 사례입니다.

문제는 코드를 수정하는 것이 전혀 두렵다는 것입니다.그리고 단위 테스트 자체는 다음과 같습니다.복잡하고 b:다루기 힘든.

그래서 저는 이 문제에 대한 대안적인 접근 방식을 생각하려고 노력하고 있는데 아마도 제가 이 문제를 잘못된 방식으로 다루고 있다는 생각이 들었습니다.어쩌면 결과에 더 집중해야 할 수도 있습니다. IE:내가 생성한 코드가 실제로 실행되고 내가 원하는 대로 작동합니까? 코드가 내가 원하는 대로 보이나요?

이와 비슷한 경험을 공유하고 싶은 사람이 있나요?

도움이 되었습니까?

해결책

나는 내 자신의 코드 생성기에 대한 내 경험을 요약하기 시작했고, 돌아가서 귀하의 질문을 다시 읽었으며 귀하가 이미 동일한 문제를 직접 다루었음을 발견했습니다. 코드 레이아웃/모양 대신 실행 결과에 중점을 두었습니다.

문제는 테스트하기 어렵고, 생성된 코드가 단위 테스트 시스템 환경에서 실제로 실행하기에 적합하지 않을 수 있으며, 예상되는 결과를 어떻게 인코딩합니까?

코드 생성기를 더 작은 조각으로 나누고 이를 단위 테스트해야 한다는 것을 알았습니다.전체 코드 생성기의 단위 테스트는 단위 테스트보다는 통합 테스트에 더 가깝습니다.

다른 팁

"단위 테스트"는 테스트의 한 종류일 뿐이라는 점을 기억하세요.단위 테스트를 할 수 있어야 합니다. 내부 코드 생성기의 일부입니다.여기서 실제로 보고 있는 것은 시스템 수준 테스트(일명회귀 테스트).단순한 의미론이 아닙니다.사고방식, 접근 방식, 기대치 등이 다릅니다.확실히 더 많은 작업이 필요하지만 아마도 총알을 물리고 엔드투엔드 회귀 테스트 스위트를 설정해야 할 것입니다.고정 C++ 파일 -> SWIG 인터페이스 -> Python 모듈 -> 알려진 출력.예상되는 출력(최종 Python 프로그램에서 나오는 내용)과 알려진 입력(고정 C++ 코드)을 확인하고 싶을 것입니다.코드 생성기 결과를 직접 확인하는 것은 객체 파일을 비교하는 것과 같습니다...

예, 결과만이 중요합니다.진짜 일은 생성된 코드를 독립적으로 실행할 수 있는 프레임워크를 작성하는 것입니다.거기에서 시간을 보내십시오.

*nux에서 실행 중인 경우 bash 스크립트나 makefile을 위해 단위 테스트 프레임워크를 덤프하는 것을 고려할 수 있습니다.Windows에서는 생성기를 실행한 다음 코드를 (다른 프로세스로) 사용하고 이를 단위 테스트하는 셸 앱/함수를 구축하는 것을 고려할 수 있습니다.

세 번째 옵션은 코드를 생성한 다음 단위 테스트만 포함하는 앱을 빌드하는 것입니다.다시 말하지만, 각 입력에 대해 이것을 실행하려면 쉘 스크립트나 기타 등등이 필요합니다.예상되는 동작을 인코딩하는 방법에 관해서는 C++ 인터페이스가 아닌 생성된 인터페이스를 사용하여 C++ 코드에 대해 수행하는 것과 거의 동일한 방식으로 수행할 수 있다는 생각이 들었습니다.

결과를 확인하면서 세부적인 테스트를 수행할 수 있다는 점을 지적하고 싶었습니다.일부 설정 및 확인 코드 내에 코드를 중첩하여 개별 코드 청크를 테스트할 수 있습니다.

int x = 0;
GENERATED_CODE
assert(x == 100);

생성된 코드를 더 작은 청크로 조합하고 청크가 자주 변경되지 않는 경우 더 많은 조건을 실행하고 좀 더 효과적으로 테스트할 수 있으며 한 청크의 세부 사항을 변경할 때 모든 테스트가 중단되는 것을 방지할 수 있습니다.

단위 테스트는 특정 단위를 테스트하는 것입니다.따라서 클래스 A에 대한 사양을 작성하는 경우 클래스 A에 클래스 B 및 C의 실제 구체적인 버전이 없는 것이 이상적입니다.

좋아 나중에 보니 이 질문의 태그에 C++/Python이 포함되어 있지만 원칙은 동일합니다.

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

위의 시스템 A는 시스템 B와 C에 인터페이스를 주입하기 때문에 다른 시스템에서 실제 기능을 실행하지 않고 시스템 A만 단위 테스트할 수 있습니다.이것은 단위 테스트입니다.

다음은 각 동작 부분에 대해 서로 다른 When 사양을 사용하여 생성부터 완료까지 시스템에 접근하는 영리한 방법입니다.

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate<string>();
        this._returnB = randomGenerator.Generate<string>();
        this._returnC = randomGenerator.Generate<string>();

        Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

결론적으로, 단일 유닛/사양은 여러 동작을 가질 수 있으며, 유닛/시스템을 개발함에 따라 사양이 커집니다.테스트 중인 시스템이 내부의 다른 구체적인 시스템에 의존하는 경우 주의하세요.

내 추천은 이미 존재하는 몇 가지 간단한 사례와 같은 알려진 입출력 결과 집합을 파악하는 것입니다. 생성된 코드를 단위 테스트.생성기를 변경하면 생성되는 정확한 문자열이 약간 다를 수 있다는 것은 전적으로 가능합니다.하지만 당신이 정말로 관심을 갖는 것은 그것이 같은 방식으로 해석되는지 여부입니다.따라서 해당 코드가 기능인 경우 테스트하는 것처럼 결과를 테스트하면 원하는 방식으로 성공하는지 확인할 수 있습니다.

기본적으로, 정말로 알고 싶은 것은 가능한 모든 조합을 물리적으로 테스트하지 않고도 발전기가 기대한 것을 생성할 수 있는지 여부입니다(또한:불가능한).생성기가 예상한 방식으로 일관되게 유지되면 생성기가 점점 더 복잡한 상황에서도 성공할 것이라는 확신을 가질 수 있습니다.

이러한 방식으로 일련의 회귀 테스트(계속 올바르게 작동해야 하는 단위 테스트)를 구축할 수도 있습니다.이렇게 하면 생성기 변경으로 인해 다른 형태의 코드가 손상되지 않는지 확인하는 데 도움이 됩니다.단위 테스트에서 포착하지 못한 버그가 발생하면 비슷한 손상을 방지하기 위해 해당 버그를 포함할 수 있습니다.

나는 그것을 어떻게 생성하는가보다 무엇을 생성하고 있는지 테스트해야 한다고 생각합니다.

내 경우에는 프로그램이 웹 애플리케이션으로 컴파일되는 다양한 유형의 코드(C#, HTML, SCSS, JS 등)를 생성합니다.전반적으로 회귀 버그를 줄이는 가장 좋은 방법은 생성기를 테스트하는 것이 아니라 웹 애플리케이션 자체를 테스트하는 것입니다.

오해하지 마세요. 생성기 코드 중 일부를 확인하는 단위 테스트가 아직 남아 있지만, 우리가 얻은 가장 큰 이익은 생성된 앱 자체에 대한 UI 테스트였습니다.

이를 생성하고 있으므로 JS에서 앱을 프로그래밍 방식으로 테스트하는 데 사용할 수 있는 멋진 추상화도 생성합니다.우리는 여기에 설명된 몇 가지 아이디어를 따랐습니다. http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

가장 큰 장점은 코드 생성부터 실제로 생성 중인 항목까지 시스템 전체를 실제로 테스트한다는 것입니다.테스트가 실패하면 발전기가 고장난 위치를 쉽게 추적할 수 있습니다.

꽤 달콤해요.

행운을 빌어요!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top