ASP.NET MVC에서 컨트롤러를 단위 테스트하는 데 실질적인 가치가 있습니까?


33

나는이 질문이 흥미로운 답변을 제공하기를 바랍니다.

ASP.NET MVC에서 컨트롤러를 단위 테스트하는 데 실질적인 가치가 있습니까?

내가 의미하는 바는 대부분의 경우 (그리고 나는 천재가 아님) 컨트롤러 방법은 심지어 가장 복잡한 것입니다.

public ActionResult Create(MyModel model)
{
    // start error list
    var errors = new List<string>();

    // check model state based on data annotations
    if(ModelState.IsValid)
    {
        // call a service method
        if(this._myService.CreateNew(model, Request.UserHostAddress, ref errors))
        {
            // all is well, data is saved, 
            // so tell the user they are brilliant
            return View("_Success");
        }
    }

    // add errors to model state
    errors.ForEach(e => ModelState.AddModelError("", e));

    // return view
    return View(model);
}

대부분의 무거운 작업은 MVC 파이프 라인 또는 서비스 라이브러리에서 수행됩니다.

따라서 물어볼 질문은 다음과 같습니다.

  • 이 방법을 테스트하는 단위의 가치는 무엇입니까?
  • NullReferenceException Request.UserHostAddressModelState함께 중단되지 않습니까? 이것들을 조롱하려고해야합니까?
  • 이 방법을 재사용 할 수있는 "도우미"로 굴절 시키면 (내가 몇 번이나 할 것인지를 고려할 것입니다!) 실제로 테스트하는 모든 것이 대부분 "파이프 라인"일 때 가치가있는 테스트 일 것입니다. 아마도 Microsoft에 의해 수명의 1 인치 이내에 테스트 되었습니까?

내 요점은 실제로 다음을 수행하는 것이 전혀 의미가없고 잘못 된 것 같습니다.

[TestMethod]
public void Test_Home_Index()
{
    var controller = new HomeController();
    var expected = "Index";
    var actual = ((ViewResult)controller.Index()).ViewName;
    Assert.AreEqual(expected, actual);
}

분명히 나는이 과장되고 무의미한 예에 둔감하고 있지만 여기에 추가 할 지혜가 있습니까?

기대하고 있습니다 ... 감사합니다.


시간과 돈이 무한하지 않다면 특정 테스트에 대한 투자 수익 (ROI)이 그만한 가치가 없다고 생각합니다. 나는 케빈이 깨뜨릴 가능성이 높은 것들을 확인하거나 자신을 가지고 무언가를 리팩토링하거나 오류 전파가 예상대로 일어나도록 도와주는 테스트를 작성할 것이다. 필요한 경우 파이프 라인 테스트는보다 글로벌 / 인프라 수준에서 수행 할 수 있으며 개별 방법 수준에서는 가치가 거의 없습니다. 그들은 가치가 없다고 말하지 않고 "작다". 따라서 귀하의 경우에 좋은 RoI를 제공한다면, 더 큰 물고기를 먼저 잡으십시오!
Mr.

답변:


18

아주 간단한 것조차도 단위 테스트는 여러 가지 목적을 수행합니다.

  1. 자신감, 쓰여진 것은 예상되는 결과에 부합합니다. 올바른 견해를 반환하는지 확인하는 것은 사소한 것처럼 보이지만 결과는 요구 사항이 충족되었다는 객관적인 증거입니다.
  2. 회귀 테스트. Create 메소드를 변경해야하는 경우에도 예상되는 출력에 대한 단위 테스트가 있습니다. 예, 출력이 변경 될 수 있으며 취성 테스트가 이루어 지지만 관리되지 않은 변경 제어에 대한 점검입니다.

특정 작업을 위해 다음을 테스트합니다.

  1. _myService가 null 인 경우 어떻게됩니까?
  2. _myService.Create에서 예외가 발생하면 처리 할 특정 예외가 발생합니까?
  3. 성공적인 _myService.Create가 _Success보기를 리턴합니까?
  4. 오류가 ModelState까지 전파됩니까?

NullReferenceException에 대한 요청 및 모델을 확인하는 것을 지적했으며 ModelState.IsValid가 Model의 NullReference를 처리 할 것이라고 생각합니다.

요청을 처리하면 일반적으로 생각하는 프로덕션에서는 불가능하지만 단위 테스트에서 발생할 수있는 Null 요청을 방지 할 수 있습니다. 통합 테스트에서는 다른 UserHostAddress 값을 제공 할 수 있습니다 (요청은 제어에 관한 한 여전히 사용자 입력이며 이에 따라 테스트해야합니다)


안녕 케빈, 시간을 내 주셔서 감사합니다. 나는 다른 사람이 다른 것을 가지고 있는지 확인하기 위해 잠시 동안 떠날 것입니다. 그러나 지금까지 당신의 것이 가장 논리적 / 명확합니다.
LiverpoolsNumber9 9

Spifty. 도움이되어 다행입니다.
Kevin

3

내 컨트롤러도 매우 작습니다. 컨트롤러의 "논리"는 대부분 필터 속성 (내장 및 직접 작성)을 사용하여 처리됩니다. 따라서 내 컨트롤러에는 일반적으로 소수의 작업 만 있습니다.

  • HTTP 쿼리 문자열, 양식 값 등에서 모델을 만듭니다.
  • 몇 가지 기본 유효성 검사 수행
  • 내 데이터 또는 비즈니스 계층으로 전화
  • 생성 ActionResult

대부분의 모델 바인딩은 ASP.NET MVC에 의해 자동으로 수행됩니다. DataAnnotations도 대부분의 유효성 검사를 처리합니다.

테스트 할 것이 거의 없더라도 나는 여전히 그것들을 작성합니다. 기본적으로 리포지토리가 호출되고 올바른 ActionResult유형이 반환 되는지 테스트합니다 . ViewResult올바른 뷰 경로가 반환되고 뷰 모델이 예상대로 보이는지 확인 하는 편리한 방법 이 있습니다. 올바른 컨트롤러 / 액션이 설정되어 있는지 확인하는 다른 방법이 RedirectToActionResult있습니다. JsonResult등에 대한 다른 테스트가 있습니다 .

클래스를 서브 Controller클래 싱 한 불행한 결과 는 HttpContext내부적으로 사용하는 많은 편리한 메소드를 제공한다는 것 입니다. 이로 인해 컨트롤러를 단위 테스트하기가 어렵습니다. 이러한 이유로 나는 일반적으로 HttpContext인터페이스 뒤에 종속 호출을하고 해당 인터페이스를 컨트롤러의 생성자에게 전달합니다 (Ninject 웹 확장을 사용하여 컨트롤러를 만듭니다). 이 인터페이스는 일반적으로 세션, 구성 설정, IPrinciple 및 URL 도우미에 액세스하기위한 도우미 속성을 사용합니다.

이것은 많은 실사를 필요로하지만 그만한 가치가 있다고 생각합니다.


시간을내어 답변을 보내 주셔서 감사합니다. 첫째, 단위 테스트에서 "도우미 방법"은 위험합니다. 둘째, "내 저장소가 호출되는지 테스트"-의존성 주입을 통해 의미합니까?
LiverpoolsNumber9 9

편의 방법이 왜 위험한가요? 나는 BaseControllerTests그들 모두가 사는 수업이 있습니다. 리포지토리를 조롱합니다. Ninject를 사용하여 연결합니다.
트래비스 파크

도우미에서 오류나 잘못된 가정을 한 경우 어떻게됩니까? 저의 또 다른 요점은 통합 테스트 (즉, 엔드 투 엔드)만이 리포지토리의 호출 여부를 "테스트"할 수 있다는 것입니다. 단위 테스트에서는 어쨌든 수동으로 리포지토리를 "새롭게"조롱합니다.
LiverpoolsNumber9 9

저장소를 생성자에 전달합니다. 당신은 시험하는 동안 그것을 조롱합니다. 모의가 예상대로 작동하는지 확인하십시오. 간단하게 해체 도우미는 ActionResult등 전달 된 URL, 모델, 검사이야
트래비스 공원

충분히 공평 해- "내 저장소가 호출되었다는 테스트"의 의미를 약간 잘못 이해했습니다.
LiverpoolsNumber9 9

2

분명히 일부 컨트롤러는 그보다 훨씬 복잡하지만 순수하게 예제를 기반으로합니다.

myService에서 예외가 발생하면 어떻게됩니까?

부수적으로.

또한 참조로 목록을 전달하는 지혜에 의문을 제기했습니다 (c #은 어쨌든 참조로 전달하기 때문에 불필요하지만 그렇지 않은 경우에도 마찬가지입니다). 그런 다음 원하는대로 처리 할 수 ​​있습니다 (어쩌면 목록에 추가하고 싶거나 모델 오류를 추가하고 싶을 수도 있습니다).

귀하의 예에서 :

참조 오류 대신 (문자열 s) => ModelState.AddModelError ( "", s)를 수행하십시오.


언급 할 가치는, 서비스가 동일한 응용 프로그램에 상주한다고 가정하지 않으면 직렬화 문제가 발생한다는 것입니다.
Michael

서비스는 별도의 dll에 있습니다. 그러나 어쨌든, 당신은 아마 "ref"에 맞을 것입니다. 다른 시점에서 myService가 예외를 throw하는지는 중요하지 않습니다. 나는 myService를 테스트하고 있지 않다. 나는 그 방법을 별도로 테스트 할 것이다. 나는 조롱 된 myService로 ActionResult "unit"을 순수하게 테스트하는 것에 대해 이야기하고있다.
LiverpoolsNumber9 9

서비스와 컨트롤러간에 1 : 1 매핑이 있습니까? 그렇지 않은 경우 일부 컨트롤러는 여러 서비스 호출을 사용합니까? 그렇다면 이러한 상호 작용을 테스트 할 수 있습니까?
Michael

하루가 끝나면 서비스 메소드는 입력 (보통 뷰 모델 또는 심지어 문자열 / 정수)을 취하고 "일을 수행"한 다음 거짓이면 부울 / 오류를 반환합니다. 컨트롤러와 서비스 계층 사이에는 "직접"링크가 없습니다. 완전히 분리되어 있습니다.
LiverpoolsNumber9 9

예, 컨트롤러와 서비스 계층 간의 관계형 모델을 이해하려고합니다. 각 컨트롤러에 해당하는 서비스 방법이 없다고 가정하면 일부 컨트롤러가이를 사용해야 할 수도 있습니다. 하나 이상의 서비스 방법?
Michael
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.