단위 테스트는 어떻게 작동합니까?

코드를 더 강력하게 만들고 단위 테스트에 대해 읽었지만 실제로 유용한 용도를 찾는 것이 매우 어렵다는 것을 알게되었습니다. 예를 들어, Wikipedia 예는 다음과 같습니다.

public class TestAdder {
    public void testSum() {
        Adder adder = new AdderImpl();
        assert(adder.add(1, 1) == 2);
        assert(adder.add(1, 2) == 3);
        assert(adder.add(2, 2) == 4);
        assert(adder.add(0, 0) == 0);
        assert(adder.add(-1, -2) == -3);
        assert(adder.add(-1, 1) == 0);
        assert(adder.add(1234, 988) == 2222);
    }
}

원하는 결과를 수동으로 계산하고 테스트해야하기 때문에이 테스트는 전혀 쓸모가 없다고 생각합니다.

assert(adder.add(a, b) == (a+b));

그러나 이것은 테스트에서 함수 자체를 코딩하는 것입니다. 누군가가 단위 테스트가 실제로 유용한 예를 제공 할 수 있습니까? 참고로 나는 현재 ~ 10 부울과 몇 가지 정수를 가져 와서 이것을 기반으로 int 결과를 제공하는 주로 “절차”함수를 코딩하고 있습니다. 단순히 단위 테스트를 통해 테스트. 편집 : 또한 루비 코드 (내가 만들지 않은)를 포팅하는 동안 이것이 정확해야합니다.



답변

작은 단위를 테스트하는 경우 단위 테스트는 항상 맹목적으로 명백합니다.

그 이유 add(x, y)언젠가 나중에 누군가가 들어갈 때문에도, 유닛 테스트의 언급을 얻는다는 add코드가 모든 곳에서 사용되는 추가 기능을 실현하지 처리 특별 세금 로직을 넣어.

단위 테스트는 매우 연관 원칙에 대해 많은 : A가 B를 수행하고, B는 C를 않는 경우, A는 C. “A는 C를하지”않는 높은 수준의 테스트입니다. 예를 들어 다음과 같은 합법적 인 비즈니스 코드를 고려하십시오.

public void LoginUser (string username, string password) {
    var user = db.FetchUser (username);

    if (user.Password != password)
        throw new Exception ("invalid password");

    var roles = db.FetchRoles (user);

    if (! roles.Contains ("member"))
        throw new Exception ("not a member");

    Session["user"] = user;
}

언뜻보기에 이것은 매우 명확한 목적을 가지고 있기 때문에 단위 테스트를위한 멋진 방법처럼 보입니다. 그러나 약 5 가지 다른 기능을 수행합니다. 각각의 경우는 유효하고 유효하지 않은 대소 문자를 가지며 단위 테스트를 크게 순열시킵니다. 이상적으로 이것은 더 세분화됩니다.

public void LoginUser (string username, string password) {

    var user = _userRepo.FetchValidUser (username, password);

    _rolesRepo.CheckUserForRole (user, "member");

    _localStorage.StoreValue ("user", user);
}

이제 우리는 단위로 내려갑니다. 하나의 단위 테스트는에 _userRepo대해 유효한 동작을 고려 하는 것을 신경 쓰지 않고 FetchValidUser호출됩니다. 다른 테스트를 사용하여 유효한 사용자가 정확히 무엇을 구성하는지 확인할 수 있습니다. 마찬가지로 CheckUserForRole… 당신은 역할 구조가 어떻게 보이는지 알기 위해 테스트를 분리했습니다. 또한 전체 프로그램이에 묶이지 않도록 분리했습니다 Session. 여기에 빠진 조각이 모두 다음과 같이 보일 것입니다.

class UserRepository : IUserRepository
{
    public User FetchValidUser (string username, string password)
    {
        var user = db.FetchUser (username);

        if (user.Password != password)
            throw new Exception ("invalid password");

        return user;
    }
}

class RoleRepository : IRoleRepository
{
    public void CheckUserForRole (User user, string role)
    {
        var roles = db.FetchRoles (user);

        if (! roles.Contains (role))
            throw new Exception ("not a member");
    }
}

class SessionStorage : ILocalStorage
{
    public void StoreValue (string key, object value)
    {
        Session[key] = value;
    }
}

리팩토링하여 한 번에 여러 가지 작업을 수행했습니다. 이 프로그램은 기본 구조를 찢어 버리거나 (NoSQL의 데이터베이스 계층을 버릴 수 있음 Session) 스레드 안전 또는 기타 사항이 아니라면 잠금을 매끄럽게 추가하는 방법을보다 잘 지원 합니다. 또한이 세 가지 종속성을 작성하기 위해 매우 간단한 테스트를 수행했습니다.

희망이 있습니다 🙂


답변

나는 현재 ~ 10 부울과 몇 개의 정수를 취하는 “절차”함수를 코딩하고 있으며 이것을 기반으로 int 결과를 제공합니다. 단순히 테스트에서 알고리즘을 다시 코딩하는 것이 유일한 단위 테스트처럼 느껴집니다

나는 당신의 절차 함수 각각이 결정 론적이라고 확신합니다. 그래서 그것은 주어진 입력 값 세트마다 특정한 int 결과를 반환합니다. 이상적으로는 특정 입력 값 세트에 대해 어떤 결과를 받아야하는지 파악할 수있는 기능 사양이 있습니다. 부족하면 특정 입력 값 세트에 대해 루비 코드 (정확하게 작동하는 것으로 추정 됨)를 실행하고 결과를 기록 할 수 있습니다. 그런 다음 결과를 테스트에 코딩해야합니다. 테스트는 코드가 실제로 올바른 것으로 알려진 결과를 생성한다는 증거 입니다 .


답변

다른 사람이 실제 예제를 제공하지 않은 것 같습니다.

    public void testRoman() {
        RomanNumeral numeral = new RomanNumeral();
        assert( numeral.toRoman(1) == "I" )
        assert( numeral.toRoman(4) == "IV" )
        assert( numeral.toRoman(5) == "V" )
        assert( numeral.toRoman(9) == "IX" )
        assert( numeral.toRoman(10) == "X" )
    }
    public void testSqrt() {
        assert( sqrt(4) == 2 )
        assert( sqrt(9) == 3 )
    }

당신은 말한다 :

원하는 결과를 수동으로 계산하고 테스트해야하기 때문에이 테스트는 전혀 쓸모가 없다고 생각합니다.

그러나 요점은 코딩 할 때 수동 계산을 수행 할 때 실수를 할 가능성이 훨씬 적다는 것입니다.

십진수를 로마자로 변환하는 코드에서 실수를 할 가능성이 얼마나됩니까? 가능성이 높습니다. 십진수를 로마 숫자로 직접 변환 할 때 실수를 저지를 가능성이 얼마나됩니까? 별로. 우리가 수동 계산에 대해 테스트하는 이유입니다.

제곱근 함수를 구현할 때 실수를 저지를 가능성은 얼마나됩니까? 가능성이 높습니다. 손으로 제곱근을 계산할 때 실수를 할 가능성이 얼마나됩니까? 아마 더 가능성이 높습니다. 그러나 sqrt를 사용하면 계산기를 사용하여 답을 얻을 수 있습니다.

참고로 나는 현재 ~ 10 부울과 몇 가지 정수를 가져 와서 이것을 기반으로 int 결과를 제공하는 주로 “절차”함수를 코딩하고 있습니다. 단순히 단위 테스트를 통해 테스트

저는 여기서 무슨 일이 일어나고 있는지 추측 할 것입니다. 함수는 다소 복잡하므로 입력에서 출력이 무엇인지 파악하기가 어렵습니다. 그렇게하려면 출력이 무엇인지 알아 내기 위해 수동으로 함수를 실행해야합니다. 당연히, 그것은 쓸모없고 오류가 발생하기 쉬운 것 같습니다.

열쇠는 올바른 출력을 찾으려는 것입니다. 그러나 올바른 것으로 알려진 출력에 대해 해당 출력을 테스트해야합니다. 그것을 계산하기 위해 자신의 알고리즘을 작성하는 것은 좋지 않습니다. 이 경우 값을 수동으로 계산하기가 너무 어렵습니다.

루비 코드로 돌아가서 다양한 매개 변수를 사용하여 이러한 원래 기능을 실행합니다. 루비 코드의 결과를 가져 와서 단위 테스트에 넣었습니다. 그렇게하면 수동 계산을 수행 할 필요가 없습니다. 그러나 원래 코드를 테스트하고 있습니다. 결과를 동일하게 유지하는 데 도움이되지만 원본에 버그가 있으면 도움이되지 않습니다. 기본적으로 sqrt 예제에서 원래 코드를 계산기처럼 취급 할 수 있습니다.

실제 코드를 보여 주면 문제에 접근하는 방법에 대한 자세한 피드백을 제공 할 수 있습니다.


답변

내가 할 수있는 유일한 단위 테스트처럼 단순히 테스트에서 알고리즘을 다시 코딩하는 것입니다.

당신은 그런 간단한 수업에 거의 맞습니다.

더 복잡한 계산기를 사용해보십시오. 볼링 점수 계산기처럼.

테스트 할 시나리오가 다른 복잡한 “비즈니스”규칙이 있으면 단위 테스트의 가치를보다 쉽게 ​​확인할 수 있습니다.

나는 당신이 밀 계산기 실행을 테스트해서는 안된다고 말하는 것이 아닙니다 (계산기가 계정을 1/3과 같은 값으로 나타낼 수 없습니까? 0으로 나누면 어떻게됩니까?). 더 많은 지점을 가지고 무언가를 테스트하면 더 많은 가치를 얻을 수 있습니다.


답변

100 % 코드 적용 범위에 대한 종교적 열의에도 불구하고, 모든 방법이 단위 테스트되지는 않는다고 말할 것입니다. 의미있는 비즈니스 로직을 포함하는 기능 만 단순히 숫자를 더하는 함수는 테스트 할 무의미합니다.

나는 현재 ~ 10 부울과 몇 개의 정수를 취하는 “절차”함수를 코딩하고 있으며 이것을 기반으로 int 결과를 제공합니다.

거기에 당신의 진짜 문제가 있습니다. 단위 테스트가 부자연 스럽거나 무의미 해 보이면 설계 결함으로 인한 것일 수 있습니다. 더 객체 지향적 인 경우 메소드 서명은 그렇게 크지 않으며 테스트 할 입력이 적습니다.

OO에 갈 필요는 없습니다. 절차 적 프로그래밍보다 우수합니다 …


답변

내 관점에서 단위 테스트는 작은 가산기 클래스에서도 유용합니다. 알고리즘을 “레코딩”한다고 생각하지 말고 알고있는 유일한 지식을 가진 블랙 박스로 생각하십시오. 빠른 곱셈을 사용하면 “a * b”)와 공용 인터페이스를 사용하는 것보다 더 빠르지 만 더 복잡한 시도를 알고 있습니다. “도대체 무엇이 잘못 될 수 있는가?”

대부분의 경우 테두리에서 발생합니다 (이 패턴을 ++,-, +-, 00-시간을 추가하여 테스트하여-+, 0+, 0-, +0, -0으로 완료했습니다). 거기에 더하거나 뺄 때 (음수 추가); MAX_INT와 MIN_INT에서 어떤 일이 발생하는지 생각해보십시오. 또는 테스트가 0에서 발생하는 상황을 정확히 확인하십시오.

간단한 클래스의 경우 모든 비밀은 매우 간단합니다 (아마도 복잡한 클래스도 가능합니다)) : 클래스의 계약에 대해 생각하고 (계약 별 디자인 참조) 테스트하십시오. 당신의 inv ‘s, pre ‘s and post가 테스트가 완료 될 “완전한”것을 더 잘 알수록 좋습니다.

테스트 클래스에 대한 힌트 : 메소드에 단 하나의 assert 만 작성하십시오. 코드 변경 후 테스트가 실패 할 때 최상의 피드백을 얻을 수 있도록 메소드에 올바른 이름 (예 : “testAddingToMaxInt”, “testAddingTwoNegatives”)을 지정하십시오.


답변

수동으로 계산 된 반환 값을 테스트하거나 테스트의 논리를 복제하여 예상 반환 값을 계산하는 대신 예상 속성에 대한 반환 값을 테스트하십시오.

예를 들어, 행렬을 반전시키는 메소드를 테스트하려는 경우 입력 값을 수동으로 반전시키지 않으려면 반환 값에 입력을 곱하고 항등 행렬을 가져 오는지 확인해야합니다.

이 방법을 메서드에 적용하려면 반환 값이 입력에 대해 어떤 속성을 가질 것인지 식별하기 위해 그 목적과 의미를 고려해야합니다.