programing tip

단위 테스트를 작성할 때 무엇을 테스트해야하는지 어떻게 알 수 있습니까?

itbloger 2020. 7. 4. 11:08
반응형

단위 테스트를 작성할 때 무엇을 테스트해야하는지 어떻게 알 수 있습니까? [닫은]


C #을 사용하려면 User사용자 이름, 비밀번호, 활성 플래그, 이름, 성, 이름 등이 있는 클래스가 필요합니다 .

사용자 인증 하고 저장 하는 방법이 있어야합니다 . 방금 메소드에 대한 테스트를 작성합니까? 그리고 속성이 .Net의 getter 및 setter이므로 속성 테스트에 대해 걱정해야합니까?


이것에 대한 많은 훌륭한 반응은 또한 나의 질문에있다 : " TDD 시작-도전? 솔루션? 추천? "

블로그 게시물 (내 질문에 부분적으로 영향을 받음)을 살펴 보는 것도 좋은 의견입니다. 즉:

어디서부터 시작해야할지 모르겠습니까?

  • 새로 시작하십시오. 새 코드를 작성할 때 테스트 작성에 대해서만 생각하십시오. 이전 코드를 다시 작업하거나 완전히 새로운 기능 일 수 있습니다.
  • 간단하게 시작하십시오. TDD와 마찬가지로 테스팅 프레임 워크에서 벗어나려고하지 마십시오. Debug.Assert가 제대로 작동합니다. 시작점으로 사용하십시오. 프로젝트를 엉망으로 만들거나 종속성을 만들지 않습니다.
  • 긍정적으로 시작하십시오. 당신은 당신의 기술을 향상시키기 위해 노력하고 있습니다. 나는 정체에 기뻐하고 스스로를 개선하기 위해 새로운 것을 시도하지 않는 많은 개발자를 보았습니다. 당신은 옳은 일을하고 있으며, 이것을 기억하면 포기하는 것을 막을 수 있습니다.
  • 도전을위한 준비를 시작하십시오. 테스트를 시작하기가 매우 어렵습니다. 도전을 기대하되, 도전은 극복 할 수 있습니다.

기대하는 것만 테스트

처음 시작할 때 발생할 수있는 모든 가능한 문제를 파악한 다음 테스트하고 수정하려고했기 때문에 실제로 문제가 발생했습니다. 이것은 두통에 대한 빠른 방법입니다. 테스트는 실제 YAGNI 프로세스 여야합니다. 문제가 있음을 알고 있으면 테스트를 작성하십시오. 그렇지 않으면 귀찮게하지 마십시오.

한 가지만 테스트

각 테스트 사례는 한 가지만 테스트해야합니다. 테스트 케이스 이름에 "및"을 넣는 것을 발견하면 뭔가 잘못한 것입니다.

나는 이것이 "getters and setters"에서 나아갈 수 있다는 것을 의미한다. :)


언어가 아닌 코드를 테스트하십시오.

다음과 같은 단위 테스트 :

Integer i = new Integer(7);
assert (i.instanceOf(integer));

컴파일러를 작성 중이고 instanceof메소드가 작동하지 않을 가능성이 0이 아닌 경우에만 유용합니다 .

언어에 의존하여 시행 할 수있는 것은 테스트하지 마십시오. 귀하의 경우, 귀하의 인증 및 저장 방법에 중점을 둘 것입니다-그리고 그 필드 중 일부 또는 전부에서 null 값을 정상적으로 처리 할 수 ​​있도록 테스트를 작성했습니다.


이것은 나를 단위 테스트로 이끌었고 매우 행복해졌습니다.

방금 단위 테스트를 시작했습니다. 오랫동안 나는 그것을 시작하는 것이 좋을 것이라는 것을 알았지 만 시작하는 방법과 더 중요한 것이 무엇인지 테스트하지 못했습니다.

그런 다음 회계 프로그램에서 중요한 코드를 다시 작성해야했습니다. 이 부분은 다양한 시나리오를 포함하기 때문에 매우 복잡했습니다. 내가 말하는 부분은 이미 회계 시스템에 입력 된 판매 및 / 또는 구매 송장을 지불하는 방법입니다.

지불 옵션이 너무 많기 때문에 코딩을 시작하는 방법을 몰랐습니다. 인보이스는 100 달러 일 수 있지만 고객은 99 달러 만 송금했습니다. 고객에게 판매 송장을 보냈지 만 해당 고객으로부터 구매 송장을 받았을 수도 있습니다. 그래서 당신은 그를 300 달러에 팔았지만 100 달러에 샀습니다. 고객은 잔액을 정산하기 위해 $ 200를 지불 할 것으로 예상 할 수 있습니다. 500 달러에 판매했지만 고객이 250 달러 만 지불하면 어떻게됩니까?

그래서 하나의 시나리오가 완벽하게 작동하지만 다른 유형의 송장 / 지불 조합에서는 잘못 될 수있는 많은 가능성으로 해결해야 할 매우 복잡한 문제가있었습니다.

이곳에서 유닛 테스트가 시작되었습니다.

판매 및 구매에 대한 송장 목록을 작성하는 방법을 테스트 코드 안에 작성하기 시작했습니다. 그런 다음 실제 지불을 만드는 두 번째 방법을 작성했습니다. 일반적으로 사용자는 사용자 인터페이스를 통해 해당 정보를 입력합니다.

그런 다음 첫 번째 TestMethod를 작성하여 지불 할인없이 단일 송장의 매우 간단한 지불을 테스트했습니다. 시스템의 모든 조치는 은행 지불이 데이터베이스에 저장 될 때 발생합니다. 보시다시피 송장을 만들고, 지불 (은행 거래)을 만들고 거래를 디스크에 저장했습니다. 내 주장에 은행 거래와 연결된 송장에 올바른 숫자가 무엇인지 입력했습니다. 거래 후 지불 횟수, 지불 금액, 할인 금액 및 송장 잔액을 확인합니다.

테스트가 실행 된 후 데이터베이스로 이동하여 예상 한 것이 있는지 다시 확인했습니다.

테스트를 작성한 결제 방법 (BankHeader 클래스의 일부)을 코딩하기 시작했습니다. 코딩에서 나는 첫 번째 테스트를 통과시키기 위해 코드로 귀찮게했습니다. 다른 복잡한 시나리오에 대해서는 아직 생각하지 않았습니다.

첫 번째 테스트를 실행하고 테스트가 통과 될 때까지 작은 버그를 수정했습니다.

그런 다음 두 번째 테스트를 작성하기 시작했습니다. 이번에는 지불 할인으로 작업합니다. 테스트를 작성한 후 할인을 지원하기 위해 지불 방법을 수정했습니다.

지불 할인으로 정확성을 테스트하는 동안 간단한 지불도 테스트했습니다. 두 테스트 모두 통과해야합니다.

그런 다음 더 복잡한 시나리오로 진행했습니다.

1) 새로운 시나리오를 생각하십시오

2) 해당 시나리오에 대한 테스트 작성

3) 단일 테스트를 실행하여 통과하는지 확인하십시오.

4) 그렇지 않은 경우 코드를 디버깅하고 통과 할 때까지 수정합니다.

5) 코드를 수정하는 동안 모든 테스트를 계속 실행했습니다.

이것이 내가 매우 복잡한 지불 방법을 만드는 방법입니다. 단위 테스트가 없으면 코딩을 시작하는 방법을 몰랐지만 문제는 압도적이었습니다. 테스트를 통해 간단한 방법으로 시작하여 더 간단한 시나리오가 여전히 작동한다는 확신을 가지고 단계별로 확장 할 수있었습니다.

단위 테스트를 사용하면 며칠 (또는 몇 주) 코딩이 절약되고 내 방법의 정확성을 어느 정도 보장 할 수 있습니다.

나중에 새 시나리오를 생각하면 테스트에 추가하여 작동하는지 여부를 확인할 수 있습니다. 그렇지 않으면 코드를 수정할 수 있지만 다른 시나리오가 여전히 올바르게 작동하는지 확인하십시오. 유지 관리 및 버그 수정 단계에서 며칠이 절약됩니다.

예, 테스트하지 않은 코드조차도 사용자가 생각하지 않았거나 행동을 방해하는 일을하는 경우에도 버그가있을 수 있습니다

다음은 결제 수단을 테스트하기 위해 만든 일부 테스트입니다.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

그들이 실제로 사소한 경우 테스트를 귀찮게하지 마십시오. 예를 들어, 이와 같이 구현 된 경우

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

반면에 getter / setter에서 암호를 암호화 및 해독하는 등 영리한 작업을 수행하는 경우 테스트를 수행하십시오.


규칙은 작성하는 모든 논리를 테스트해야한다는 것입니다. 게터와 세터에서 특정 기능을 구현했다면 테스트 할 가치가 있다고 생각합니다. 그들이 일부 개인 필드에만 값을 할당하는 경우 귀찮게하지 마십시오.


이 질문은 어떤 메소드가 테스트되고 어떤 메소드를 테스트하지 않는지에 대한 질문입니다.

가치 할당을위한 세터와 게터는 일관성과 미래의 성장을 염두에두고 만들어졌으며, 시간이 지날수록 세터 / 게터가 더 복잡한 작업으로 발전 할 수 있다고 예측했습니다. 일관성과 미래 성장을 위해 이러한 방법에 대한 단위 테스트를 실시하는 것이 합리적입니다.

추가 기능을 추가하기 위해 변경하는 동안 코드 안정성이 주요 목표입니다. 테스트 방법론에 세터 / 게터를 포함하여 해고당한 사람은 아무도 모르지만, 마지막으로 알고 있거나 기억할 수있는 방법을 테스트 해보고 싶었던 사람들이 있다고 확신합니다. 더 긴 경우.

어쩌면 팀의 다른 구성원이 set / get 메소드를 확장하여 테스트가 필요하지만 테스트를 작성하지 않은 로직을 포함 할 수있었습니다. 그러나 이제 코드에서 이러한 메소드를 호출하고 있으며 메소드가 변경되어 심층적 인 테스트가 필요하다는 것을 알지 못하며 개발 및 QA에서 수행하는 테스트는 결함을 유발하지 않지만 릴리스 첫 날의 실제 비즈니스 데이터는 트리거하십시오.

두 팀원은 이제 공을 떨어 뜨 렸고 실패했을 수 있지만 유닛 테스트로 다루지 않는 로직을 포함하도록 세트 / 겟이 모핑 될 때 누가 유닛 테스트에 참여하지 않았는지 토론 할 것입니다. 처음 세트 / get을 작성한 팀원은 테스트가 간단한 set / gets에서 첫날부터 구현 된 경우이 정리에서 더 쉬운 시간을 갖습니다.

제 생각에는 단위 테스트, 심지어 사소한 방법으로 모든 방법을 다루는 몇 분의 "폐기"시간이 길을 잃는 두통과 비용 손실 / 사업 손실 및 다른 사람의 직업 손실을 줄일 수 있다고 생각합니다.

하급 팀원이 사소한 방법을 사소하지 않은 방법으로 변경하고 테스트를 업데이트하라는 메시지를 표시하면 사소한 방법으로 단위 테스트를 래핑했다는 사실을 알 수 있습니다. 결함이 포함되어 있기 때문에 아무도 문제가 없습니다. 생산에 도달하지 못했습니다.

우리가 코딩하는 방식과 우리의 코드에서 볼 수있는 규율은 다른 사람들을 도울 수 있습니다.


또 다른 정식 답변. 이것은 론 제프리스의 생각입니다.

작업하려는 코드 만 테스트하십시오.


상용구 코드를 테스트하는 것은 시간 낭비이지만 Slavo에 따르면 게터 / 세터에 부작용을 추가하면 해당 기능과 함께 테스트를 작성해야합니다.

테스트 중심 개발을 수행하는 경우 먼저 계약 (예 : 인터페이스)을 작성하고 테스트를 작성하여 예상되는 결과 / 동작을 문서화하는 해당 인터페이스를 실습해야합니다. 그런 다음 단위 테스트에서 코드를 건드리지 않고 메서드를 직접 작성하십시오. 마지막으로 코드 커버리지 도구를 잡고 테스트에서 코드의 모든 논리 경로를 테스트해야합니다.


개인 필드를 설정하는 것보다 추가 동작이없는 게터 및 세터와 같은 사소한 코드는 테스트하기가 너무 어렵습니다. 3.0에서 C #에는 컴파일러가 개인 필드를 관리하는 구문 설탕이 있으므로 프로그래밍 할 필요가 없습니다.

나는 보통 수업에서 기대하는 행동을 확인하는 매우 간단한 테스트를 많이 작성합니다. 두 개의 숫자를 추가하는 것과 같은 간단한 경우에도 마찬가지입니다. 간단한 테스트 작성과 몇 줄의 코드 작성간에 많은 전환이 이루어졌습니다. 그 이유는 내가 생각하지 않은 것을 깨뜨릴 것을 두려워하지 않고 코드를 바꿀 수 있기 때문입니다.


모든 것을 테스트해야합니다. 지금 당신은 게터와 세터를 가지고 있지만 언젠가는 그것들을 약간 변경하거나 유효성 검사 또는 다른 것을 할 수 있습니다. 오늘 작성하는 테스트는 내일 사용되어 모든 것이 평상시처럼 작동하는지 확인합니다. 테스트를 작성할 때 "지금은 사소한 것"과 같은 고려 사항을 잊어야합니다. 민첩하거나 테스트 중심의 컨텍스트에서 향후 리팩토링을 가정하고 테스트해야합니다. 또한 매우 긴 문자열 또는 다른 "나쁜"내용과 같은 이상한 값을 입력하려고 했습니까? 글쎄, 나중에는 코드가 얼마나 악용 될 수 있다고 가정해서는 안됩니다.

일반적으로 광범위한 사용자 테스트를 작성하는 것이 힘들다는 것을 알았습니다. 반면에 항상 응용 프로그램이 어떻게 작동 해야하는지에 대한 귀중한 통찰력을 제공하고 쉬운 가정을 버리는 데 도움이됩니다 (예 : 사용자 이름의 길이는 항상 1000 자 미만입니다).


툴킷 또는 오픈 소스 유형의 프로젝트로 끝나는 간단한 모듈의 경우 사소한 게터와 세터를 포함하여 가능한 한 많이 테스트해야합니다. 명심해야 할 것은 특정 모듈을 작성할 때 단위 테스트를 생성하는 것이 매우 간단하고 간단하다는 것입니다. 게터와 세터를 추가하는 것은 최소한의 코드이며 많은 생각없이 처리 할 수 ​​있습니다. 그러나 코드가 더 큰 시스템에 배치되면 이러한 추가 노력으로 기본 클래스의 유형 변경과 같은 기본 시스템의 변경으로부터 보호 할 수 있습니다. 모든 것을 테스트하는 것이 완벽한 회귀를 갖는 가장 좋은 방법입니다.


게터와 세터에 대한 단위 테스트를 작성하는 것이 아프지 않습니다. 현재는 현장에서 필드 가져 오기 / 설정을 수행하고있을 수도 있지만 향후에는 검증해야 할 유효성 검증 논리 또는 속성 간 종속성이있을 수 있습니다. 당신이 그것에 대해 생각하고 그때가되면 다시 개조하는 것을 기억하면서 지금 그것을 작성하는 것이 더 쉽습니다.


일반적으로 특정 값에 대해서만 방법이 정의 된 경우 허용 가능한 의 경계 그 밖의 값을 테스트 하십시오. 다시 말해, 메소드가 수행해야하는 작업을 수행 하지만 더 이상 수행하지 않는지 확인하십시오 . 실패 할 때는 일찍 실패하기 때문에 이것은 중요합니다.

상속 계층에서 LSP 준수 를 테스트해야 합니다.

기본 게터와 세터 테스트는 나중에 약간의 유효성 검사를 계획하지 않는 한 나에게별로 유용하지 않은 것 같습니다.


잘 깨질 수 있다고 생각되면 테스트를 작성하십시오. 나는 보통 setter / getter를 테스트하지 않지만 User.Name에 대해 하나를 만들라고 말합니다. 이름과 성을 연결하는 테스트를 작성합니다. 누군가 성과 이름의 순서를 변경하면 적어도 그가 알 것입니다. 그는 시험 된 것을 바꿨다.


정식 답변은 "깨질 수있는 모든 것을 테스트"입니다. 속성이 깨지지 않을 것이라고 확신한다면 테스트하지 마십시오.

그리고 무언가가 깨지면 (버그를 발견하면) 분명히 테스트해야 함을 의미합니다. 버그를 재현하기위한 테스트를 작성하고, 실패를 관찰 한 다음 버그를 수정 한 다음 테스트 패스를보십시오.


민첩한 개발 환경에서 단위 테스트를 이해하는 것처럼 Mike, 예. 게터와 세터를 공개적으로 볼 수 있다고 가정해야합니다. 단위 테스트의 전체 개념은이 경우 클래스 인 소프트웨어 단위를 블랙 박스 로 테스트하는 것 입니다. 게터와 세터는 외부에서 볼 수 있으므로 인증 및 저장과 함께 테스트해야합니다.


인증 및 저장 메소드가 특성을 사용하는 경우 테스트에서 간접적으로 특성을 터치합니다. 속성이 데이터에 대한 액세스 만 제공하는 한 100 % 적용 범위를 제외하고는 명시적인 테스트가 필요하지 않습니다.


나는 당신의 게터와 세터를 테스트 할 것입니다. 누가 코드를 작성하는지에 따라 getter / setter 메소드의 의미를 변경하는 사람들이 있습니다. getter 메소드의 일부로 변수 초기화 및 기타 유효성 검사를 보았습니다. 이런 종류의 것을 테스트하려면 해당 코드를 명시 적으로 다루는 단위 테스트가 필요합니다.


개인적으로 나는 "깨질 수있는 것을 테스트"하고 간단한 게터 (또는 더 나은 자동 속성)도 깨지지 않습니다. 나는 간단한 반환 진술에 실패하지 않았으므로 테스트를 해본 적이 없습니다. 게터가 그 안에 또는 다른 형태의 진술로 계산한다면, 나는 분명히 그에 대한 테스트를 추가 할 것입니다.

개인적으로 Moq 를 모의 객체 프레임 워크로 사용하고 내 객체가 주변 객체를 정상적으로 호출하는지 확인합니다.


You have to cover the execution of every method of the class with UT and check the method return value. This includes getters and setters, especially in case the members(properties) are complex classes, which requires large memory allocation during their initialization. Call the setter with some very large string for example (or something with greek symbols) and check the result is correct (not truncated, encoding is good e.t.c.)

In case of simple integers that also applies - what happens if you pass long instead of integer? That's the reason you write UT for :)


I wouldn't test the actual setting of properties. I would be more concerned about how those properties get populated by the consumer, and what they populate them with. With any testing, you have to weigh the risks with the time/cost of testing.


You should test "every non-trivial block of code" using unit tests as far as possible.

If your properties are trivial and its unlikely that someone will introduce a bug in it, then it should be safe to not unit test them.

Your Authenticate() and Save() methods look like good candidates for testing.


Ideally, you would have done your unit tests as you were writing the class. This is how you're meant to do it when using Test Driven Development. You add the tests as you implement each function point, making sure that you cover the edge-cases with test too.

Writing the tests afterwards is much more painful, but doable.

Here's what I'd do in your position:

  1. Write a basic set of tests that test the core function.
  2. Get NCover and run it on your tests. Your test coverage will probably be around 50% at this point.
  3. Keep adding tests that cover your edge-cases until you get coverage of around 80%-90%

This should give you a nice working set of unit tests that will act as a good buffer against regressions.

The only problem with this approach is that code has to be designed to be testable in this fashion. If you made any coupling mistakes early on, you won't be able to get high coverage very easily.

This is why it is really important to write the tests before you write the code. It forces you to write code that is loosely coupled.


Don't test obviously working (boilerplate) code. So if your setters and getters are just "propertyvalue = value" and "return propertyvalue" it makes no sense to test it.


Even get / set can have odd consequences, depending upon how they have been implemented, so they should be treated as methods.

Each test of these will need to specify sets of parameters for the properties, defining both acceptable and unacceptable properties to ensure the calls return / fail in the expected manner.

You also need to be aware of security gotchas, as an example SQL injection, and test for these.

So yes, you do need to worry about testing the properties.


I believe it's silly to test getters & setters when they only make a simple operation. Personally I don't write complex unit tests to cover any usage pattern. I try to write enough tests to ensure I have handled the normal execution behavior and as much error cases I can think of. I will write more unit tests as a response to bug reports. I use unit test to ensure the code meets the requirements and to make future modification easier. I feel a lot more willing to change code when I know that if I break something a test will fail.


I would write a test for anything that you are writing code for that is testable outside of the GUI interface.

Typically, any logic that I write that has any business logic I place inside another tier or business logic layer.

Then writing tests for anything that does something is easy to do.

First pass, write a unit test for each public method in your "Business Logic Layer".

If I had a class like this:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

The first thing I would do before I wrote any code knowing that I had these actions to perform would be to start writing unit tests.

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Write your tests to validate the code you've written to do something. If you iterating over a collection of things, and changing something about each of them, write a test that does the same thing and Assert that actually happened.

There's a lot of other approaches you can take, namely Behavoir Driven Development (BDD), that's more involved and not a great place to start with your unit testing skills.

So, the moral of the story is, test anything that does anything you might be worried about, keep the unit tests testing specific things that are small in size, a lot of tests are good.

Keep your business logic outside of the User Interface layer so that you can easily write tests for them, and you'll be good.

I recommend TestDriven.Net or ReSharper as both easily integrate into Visual Studio.


I would recommend writing multiple tests for your Authenticate and Save methods. In addition to the success case (where all parameters are provided, everything is correctly spelled, etc), it's good to have tests for various failure cases (incorrect or missing parameters, unavailable database connections if applicable, etc). I recommend Pragmatic Unit Testing in C# with NUnit as a reference.

As others have stated, unit tests for getters and setters are overkill, unless there's conditional logic in your getters and setters.


Whilst it is possible to correctly guess where your code needs testing, I generally think you need metrics to back up this guess. Unit testing in my view goes hand in hand with code-coverage metrics.

Code with lots of tests but a small coverage hasn't been well tested. That said, code with 100% coverage but not testing the boundry and error cases is also not great.

You want a balance between high coverage (90% minimum) and variable input data.

Remember to test for "garbage in"!

Also, a unit-test is not a unit-test unless it checks for a failure. Unit-tests that don't have asserts or are marked with known exceptions will simply test that the code doesn't die when run!

You need to design your tests so that they always report failures or unexpected/unwanted data!


It makes our code better... period!

One thing us software developers forget about when doing test driven development is the purpose behind our actions. If a unit test is being written after the production code is already in place, the value of the test goes way down (but is not completely lost).

In the true spirit for unit testing, these tests are not primarily there to "test" more of our code; or to get 90%-100% better code coverage. These are all fringe benefits of writing the tests first. The big payoff is that our production code ends be be written much better due to the natural process of TDD.

To help better communicate this idea, the following may be helpful in reading:

The Flawed Theory of Unit Tests
Purposeful Software Development

If we feel that the act of writing more unit tests is what helps us gain a higher quality product, then we may be suffering from a Cargo Cult of Test Driven Development.

참고URL : https://stackoverflow.com/questions/62625/how-do-you-know-what-to-test-when-writing-unit-tests

반응형