이야기를하는 것이 좋은 아이디어를주기 위해 단위 테스트를 사용하고 있습니까?


13

그래서 얼마 전에 작성한 인증 모듈이 있습니다. 이제 나는 길의 오류를보고 그것을 위해 단위 테스트를 작성하고 있습니다. 단위 테스트를 작성하는 동안 좋은 이름과 테스트하기 좋은 영역을 찾기가 어렵습니다. 예를 들어 다음과 같은 것이 있습니다

  • Login_should_redirect_when_not_logged_in 필요
  • Login_should_pass_through_when_logged_in 필요
  • Login_should_work_when_given_proper_credentials

개인적으로, 나는 그것이 "적절한"것처럼 보이지만 조금 추악하다고 생각합니다. 또한 스캔을 통해 테스트를 구별하는 데 어려움이 있습니다 (방금 실패한 것을 알기 위해 메소드 이름을 두 번 이상 읽어야합니다)

따라서 기능을 순전히 테스트하는 테스트를 작성하는 대신 시나리오를 다루는 일련의 테스트를 작성해야한다고 생각했습니다.

예를 들어, 이것은 내가 생각해 낸 테스트 스텁입니다.

public class Authentication_Bill
{
    public void Bill_has_no_account() 
    { //assert username "bill" not in UserStore
    }
    public void Bill_attempts_to_post_comment_but_is_redirected_to_login()
    { //Calls RequiredLogin and should redirect to login page
    }
    public void Bill_creates_account()
    { //pretend the login page doubled as registration and he made an account. Add the account here
    }
    public void Bill_logs_in_with_new_account()
    { //Login("bill", "password"). Assert not redirected to login page
    }
    public void Bill_can_now_post_comment()
    { //Calls RequiredLogin, but should not kill request or redirect to login page
    }
}

이 패턴에 대해 들었습니까? 나는 수용 이야기 등을 보았지만 이것은 근본적으로 다릅니다. 가장 큰 차이점은 테스트를 "강제"하는 시나리오를 제시한다는 것입니다. 테스트해야 할 가능한 상호 작용을 수동으로 시도하는 대신. 또한 이것이 정확히 하나의 메소드와 클래스를 테스트하지 않는 단위 테스트를 권장한다는 것을 알고 있습니다. 그래도 괜찮습니다. 또한 테스트가 서로 독립적이며 순서가 중요하지 않다고 가정하기 때문에 적어도 일부 테스트 프레임 워크에서 문제가 발생할 수 있음을 알고 있습니다 (이 경우의 위치).

어쨌든, 이것은 전혀 권장되는 패턴입니까? 아니면 이것이 "단위"테스트가 아닌 내 API의 통합 테스트에 완벽하게 맞습니까? 이것은 개인 프로젝트에 불과하기 때문에 잘 진행될 수도 있고 그렇지 않을 수도있는 실험에 개방적입니다.


4
단위, 통합 및 기능 테스트 사이의 경계가 모호 합니다. 테스트 스텁의 이름 선택해야 할 경우 작동합니다.
yannis

나는 그것이 맛의 문제라고 생각합니다. 개인적으로 나는 테스트 한 이름을 _test추가하고 주석을 사용하여 내가 기대하는 결과를 기록합니다. 개인적인 프로젝트라면 편안하다고 느끼는 스타일을 찾아 내십시오.
Mr Lister

1
나는 정렬 / 법 / 어설 패턴을 사용하여 단위 테스트를 작성하는 더 전통적인 방법에 대한 자세한 내용과 답변을 서면으로 작성했습니다,하지만 친구가 사용하는 많은 성공을했다 github.com/cucumber/cucumber/wiki/Gherkin 입니다, 사양에 사용되며 afaik은 오이 테스트를 생성 할 수 있습니다.
StuperUser

nunit 또는 이와 유사한 방법으로 보여준 방법을 사용하지는 않지만 nspec은보다 스토리 중심의 환경에서 컨텍스트를 구축하고 테스트 할 수 있도록 지원합니다. nspec.org
Mike

1
"Bill"을 "User"로 변경하면 완료됩니다
Steven A. Lowe

답변:


15

예, 테스트 할 예제 시나리오의 이름을 테스트에 제공하는 것이 좋습니다. 그리고 단위 테스트 도구를 사용하여 단순히 단위 테스트 이상을 수행하는 것만으로도 많은 사람들이 성공할 수 있습니다 (나도).

그러나 아니요, 테스트 실행 순서가 중요한 방식으로 테스트를 작성하는 것은 좋은 생각이 아닙니다. 예를 들어, NUnit을 사용하면 실행하려는 테스트를 대화식으로 선택할 수 있으므로 더 이상 의도 한 방식으로 작동하지 않습니다.

각 테스트의 주요 테스트 부분 ( "어설 션"포함)을 시스템을 올바른 초기 상태로 설정 한 부분에서 분리하면이를 쉽게 피할 수 있습니다. 위의 예를 사용하여 : 어설 션없이 계정을 만들고 로그온하고 주석을 게시하는 방법을 작성하십시오. 그런 다음 해당 방법을 다른 테스트에서 재사용하십시오. 또한 [Setup]시스템이 올바르게 정의 된 초기 상태에 있는지 확인하기 위해 테스트 픽스처 의 메소드에 코드를 추가 해야합니다 (예 : 데이터베이스에 계정이없고 연결되지 않은 사람 등).

편집 : 물론 이것은 테스트의 "이야기"본성에 위배되는 것처럼 보이지만 도우미 메서드에 의미있는 이름을 지정하면 각 테스트 내에서 이야기를 찾을 수 있습니다.

따라서 다음과 같아야합니다.

[TestFixture]
public class Authentication_Bill
{
    [Setup]
    public void Init()
    {  // bring the system in a predefined state, with noone logged in so far
    }

    [Test]
    public void Test_if_Bill_can_create_account()
    {
         CreateAccountForBill();
         // assert that the account was created properly 
    }

    [Test]
    public void Test_if_Bill_can_post_comment_after_login()
    { 
         // here is the "story" now
         CreateAccountForBill();
         LoginWithBillsAccount();
         AddCommentForBill();
        //  assert that the right things happened
    }

    private void CreateAccountForBill()
    {
        // ...
    }
    // ...
}

더 나아가서 xUnit 도구를 사용하여 기능 테스트를 실행하는 것이 좋습니다. 툴링을 테스트 유형과 혼동하지 않으면이 테스트를 실제 단위 테스트와 분리하여 개발자가 할 수 있습니다 커밋 시간에 여전히 유닛 테스트를 빠르게 실행하십시오. 이것들은 단위 테스트보다 훨씬 느릴 것입니다.
bdsl

4

단위 테스트로 스토리를 이야기 할 때의 문제점은 단위 테스트가 서로 독립적으로 배열되고 실행되어야한다는 것이 명백하지 않다는 것입니다.

좋은 단위 테스트는 다른 모든 종속 코드와 완전히 분리되어야하며 테스트 할 수있는 가장 작은 코드 단위 입니다.

이것은 코드가 작동하는지 확인하는 것뿐만 아니라 테스트가 실패하면 코드가 무료 인 위치가 정확히 어디인지 진단합니다. 테스트가 분리되지 않은 경우 무엇이 잘못되었는지 정확히 파악하고 단위 테스트의 주요 이점을 놓치려면 테스트가 의존하는 것을 살펴 봐야합니다. 실행 문제의 순서를 가짐으로써 많은 오탐을 일으킬 수 있습니다. 테스트가 실패하면 테스트가 완벽하게 제대로 수행 되었음에도 불구하고 다음 테스트가 실패 할 수 있습니다.

더 깊이있는 좋은 기사는 더티 하이브리드 테스트 의 고전입니다 .

클래스, 메소드 및 결과를 읽을 수있게하기 위해 위대한 Art of Unit 테스트 는 명명 규칙을 사용합니다.

테스트 클래스 :

ClassUnderTestTests

시험 방법 :

MethodUnderTest_Condition_ExpectedResult

각 테스트 전에 실행되는 [Setup]을 사용하는 대신 @Doc Brown의 예제를 복사하기 위해 테스트 할 격리 된 개체를 빌드하는 도우미 메서드를 작성합니다.

[TestFixture]
public class AuthenticationTests
{
    private Authentication GetAuthenticationUnderTest()
    {
        // create an isolated Authentication object ready for test
    }

    [Test]
    public void CreateAccount_WithValidCredentials_CreatesAccount()
    {
         //Arrange
         Authentication codeUnderTest = GetAuthenticationUnderTest();
         //Act
         Account result = codeUnderTest.CreateAccount("some", "valid", "data");
         //Assert
         //some assert
    }

    [Test]
    public void CreateAccount_WithInvalidCredentials_ThrowsException()
    {
         //Arrange
         Authentication codeUnderTest = GetAuthenticationUnderTest();
         Exception result;
         //Act
         try
         {
             codeUnderTest.CreateAccount("some", "invalid", "data");
         }
         catch(Exception e)
         {
             result = e;
         }
         //Assert
         //some assert
    }
}

따라서 실패한 테스트는 의미있는 이름을 가지므로 정확히 어떤 메소드가 실패했는지, 조건 및 예상 결과에 대한 설명이 제공됩니다.

그것이 항상 단위 테스트를 작성하는 방법이지만 친구는 Gerkin으로 많은 성공을 거두었습니다 .


1
나는 이것이 좋은 글이라고 생각하지만, 링크 된 기사가 "하이브리드"테스트에 대해 말하는 것에 동의하지 않습니다. "소규모"통합 테스트 ( 물론 순수한 단위 테스트에 대한 대안 은 아님)가 있으면 어떤 코드에 어떤 코드가 잘못 포함되어 있는지 정확하게 알 수없는 경우에도 IMHO가 매우 유용 할 수 있습니다. 이러한 테스트를 유지 관리 할 수있는 경우 해당 테스트의 코드가 얼마나 깨끗한 지에 따라 다르지만 "더러워"지지 않습니다. 그리고 그 테스트의 목표는 (OP의 예에서와 같이) 매우 분명 할 수 있다고 생각합니다.
Doc Brown

3

당신이 설명하는 것은 나에게 단위 테스트보다 행동 중심 디자인 (BDD) 과 더 비슷 합니다. Gherkin DSL을 기반으로하는 .NET BDD 기술인 SpecFlow 를 살펴보십시오 .

코딩에 대한 지식 없이도 모든 사람이 읽고 쓸 수있는 강력한 기능. 테스트 팀은 통합 테스트 스위트에이를 활용하여 큰 성공을 거두고 있습니다.

@DocBrown의 단위 테스트 규칙에 대해서는 @DocBrown의 대답이 확실해 보입니다.


정보를 위해, BDD는 TDD와 똑같으며, 변화하는 글쓰기 스타일 일뿐입니다. 예 : TDD = assert(value === expected)BDD = value.should.equals(expected)+ "단위 테스트 독립성"문제를 해결하는 기능을 레이어로 설명합니다. 이것은 훌륭한 스타일입니다!
Offirmo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.