일련의 'AssertEquals'보다 단위 테스트를 작성하는 더 좋은 방법이 있습니까?


12

qunit을 사용하여 단위 테스트에 필요한 기본 예는 다음과 같습니다.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<link rel="stylesheet" href="qunit/qunit-1.13.0.css">
<script src = "qunit/qunit-1.13.0.js"></script>
<script src = "../js/fuzzQuery.js"></script>

<script>

test("Fuzz Query Basics", function()
        {
            equal(fuzzQuery("name:(John Smith)"), "name:(John~ Smith~)");
            equal(fuzzQuery("name:Jon~0.1"), "name:Jon~0.1");
            equal(fuzzQuery("Jon"), "Jon~");
            //etc

        }
    );

</script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

이제 나는 이것이 조금 반복적이라고 생각했습니다.

모든 입력 / 출력을 배열에 넣고 반복 할 수 있습니다.

test("Fuzz Query Basics", function()
        {
            var equals = [
                           ["name:(John Smith)", "name:(John~ Smith~)"],
                           ["name:Jon~0.1", "name:Jon~0.1"],
                           ["Jon", "Jon~"]
                           ];

            for (var i = 0; i<equals.length; i++)
                {
                    equal(fuzzQuery(equals[i][0]), equals[i][1]);               
                }

        }
    );

그리고 이것은 잘 작동합니다.

이 두 번째 방법에 대해 생각할 수있는 유일한 장점은 실제로 사용 equal하고 싶지 않다면 한 번에 변경하기가 더 쉽다는 것입니다.

가독성 측면에서 두 번째 방법을 선호하지만 결정적인 방법은 아닙니다.

더 요약하면 입 / 출력 케이스를 별도의 CSV 파일에 넣을 수 있으므로 수정이 더 쉬워 질 수 있습니다.

문제는 이런 종류의 단위 테스트 작성에 대한 일반적인 규칙은 무엇입니까?

그것들을 배열에 넣지 말아야 할 이유가 있습니까?


이 중 어느 것이 실패한 값을 알려줍니까?
JeffO

1
@JeffO-예-QUnit 포함-테스트에 실패하면 출력에 예상 값과 실제 값이 표시됩니다.
dwjohnston

답변:


8

리팩토링 된 테스트에는 냄새가 있습니다 : Conditional Test Logic .

테스트에서 조건부 논리를 작성하지 않아야하는 이유는 두 가지입니다. 첫 번째는 링크 된 xUnit 패턴 기사에 설명 된대로 테스트 코드가 올바른지 확신 할 수 없게하는 것입니다.

두 번째는 테스트의 의미를 모호하게한다는 것입니다. 우리 는 주어진 행동을 테스트하기위한 논리를 한 곳에두기 때문에 테스트 방법을 작성 하고이를 설명적인 이름으로 지정할 수있게합니다 ( 테스트를위한 좋은 이름의 가치에 대해서는 Dan North의 원래 BDD 기사 참조 ). 테스트가 for루프가 있는 단일 함수 안에 숨겨져 있으면 판독기 코드의 의미가 모호해집니다. 독자는 루프를 이해해야 할뿐만 아니라 루프 내에서 테스트되는 모든 다른 동작을 정신적으로 풀어야합니다.

항상 그렇듯이 해결책은 추상화 수준을 높이는 것입니다. xUnit.NET 또는 Contexts do 와 같은 매개 변수화 된 테스트 를 제공하는 테스트 프레임 워크를 사용하십시오 (면책 조항 : Contexts를 작성했습니다). 이를 통해 동일한 동작에 대한 삼각 측량 테스트를 자연스럽게 그룹화 할 수 있으며, 별도의 동작에 대한 테스트는 별도로 유지합니다.


그런데 좋은 질문입니다
Benjamin Hodgson

1
1) 추상화 수준을 높이면 for 루프에 의해 가려진 것과 동일한 세부 사항을 숨기지 않습니까? 2) 매개 변수화 된 테스트가 여기에 적용되는지 확실하지 않습니다. 여기 어딘가에 병렬이있는 것처럼 보이지만 10-20 값의 데이터 세트가 있고 SUT을 통해 모든 데이터를 실행하려는 OP와 비슷한 상황이 많이 있습니다. 그렇습니다. 각 값은 다르고 잠재적으로 다른 boudaries를 테스트하지만 실제로는 모든 단일 값에 대해 "발명 된"테스트 이름이 나오는 것 같습니다. 비슷한 것을 사용하여 최적의 값 / 코드 크기 비율을 발견했습니다 ...
DXM

... 루프. 테스트가 실패하면 어설 션은 실패한 것을 정확하게 인쇄하므로 개발자는 문제를 정확하게 지적하기에 충분한 피드백을 얻습니다.
DXM

@DXM 1) 테스트 프레임 워크는 매개 변수화 된 테스트 기능을 제공합니다. 테스트 프레임 워크를 암시 적으로 신뢰하므로 테스트를 작성하지 않습니다. 2) 매개 변수화 된 테스트는 정확히이 목적을위한 것입니다. 매번 정확히 동일한 단계를 수행하지만 입력 / 출력 값이 다릅니다. 테스트 프레임 워크는 동일한 테스트 방법을 통해 다른 입력을 실행하여 각 이름을 작성할 필요가 없습니다.
Benjamin Hodgson

5

실제로 데이터 기반 단위 테스트를 원하는 것 같습니다. QUnit 사용을 언급 했으므로 매개 변수화 된 테스트를 가능하게하는 플러그인을 찾았습니다.

https://github.com/AStepaniuk/qunit-parameterize

테스트 코드 자체가 조건이 아닌 한 데이터 기반 테스트에는 이념적으로 잘못된 것이 없습니다. 테스트 코드를 보면 데이터 기반 테스트에 매우 적합한 것으로 보입니다.

GitHub README의 예제 코드 :

QUnit
    .cases([
        { a : 2, b : 2, expectedSum : 4 },
        { a : 5, b : 5, expectedSum : 10 },
        { a : 40, b : 2, expectedSum : 42 }
    ])
    .test("Sum test", function(params) {
        var actualSum = sum(params.a, params.b);
        equal(actualSum, params.expectedSum);
    });

1
합의하면 데이터 기반 테스트처럼 보입니다. 그러나 이것이 두 번째 코드 예제에서 이미 가지고있는 것처럼 보입니다.
Robert Harvey

1
@RobertHarvey-맞습니다. 그가 달성하고자하는 것에 대해 허용되는 용어가 있으며, 이러한 종류의 테스트를보다 쉽게 ​​작성하기 위해 사용되는 테스트 프레임 워크 용 플러그인이 있습니다. 나는 미래에 대한 답에 주목할 가치가 있다고 생각했다.
Greg Burghardt

1

보다 유지 관리가 쉬운 배열을 사용하면 반복 횟수가 줄어 듭니다. 내가 좋아하는 한 가지 접근법은 테스트를 정렬하고, 행동하고, 주장하는 별도의 방법을 사용하는 것이지만 테스트하는 입력 매개 변수를 허용하므로 입력 세트당 하나의 테스트 방법이 있습니다.

이를 통해 어떤 테스트 / 입력이 실패했는지 즉시 알 수 있습니다.


0

나는 당신의 두 번째 접근법을 좋아하지만 2 점을 추가 할 것입니다

  • 인덱스 작업은 깨끗하지 않으므로 배열을 사용하여 테스트 된 데이터를 저장하지 마십시오.
  • for루프를 사용하지 마십시오

`

[
    {
        process: "name:(John Smith)",
        result: "name:(John~ Smith~)"
    },
    {
        process: "name:Jon~0.1", 
        result: "name:Jon~0.1"
    },
    {
        process: "Jon", 
        result: "Jon~"
    }
]
.forEach(function(data){

    var result = fuzzQuery(data.process);
    equal(result, data.result);
});

나는 qunit에 대해 확신하지 못하지만 좋은 테스트 러너는 입력 문자열이 실패한 것과 예상 결과가 무엇인지 보여줍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.