[C#] 단위 테스트를 작성할 때 무엇을 테스트해야하는지 어떻게 알 수 있습니까? [닫은]
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에서 첫날부터 구현 된 경우이 정리에서 더 쉬운 시간을 갖습니다.
제 생각에는 단위 테스트, 심지어 사소한 방법으로 모든 방법을 다루는 몇 분의 “폐기”시간이 길을 잃는 두통과 비용 손실 / 사업 손실 및 다른 사람의 직업 손실을 줄일 수 있다고 생각합니다.
하급 팀원이 사소한 방법을 사소하지 않은 방법으로 변경하고 테스트를 업데이트하라는 메시지를 표시하면 사소한 방법으로 단위 테스트를 래핑했다는 사실을 알 수 있습니다. 결함이 포함되어 있기 때문에 아무도 문제가 없습니다. 생산에 도달하지 못했습니다.
우리가 코딩하는 방식과 코드에서 볼 수있는 규율은 다른 사람들을 도울 수 있습니다.
답변
또 다른 정식 답변. 이것은 론 제프리스의 생각입니다.
작업하려는 코드 만 테스트하십시오.