2013-10-31 5 views
1

사용자 정의 ActionResult를 테스트하려고하는데 작동하지 않습니다. 응답 스트림에 파일을 쓰고 있습니다. 따라서 단위 테스트에서 무엇을하고 싶은지 응답을 읽고 올바른지 확인하십시오.유닛 테스트 및 ActionResult

/// <summary> 
    ///  Start writing the file. 
    /// </summary> 
    /// <param name="response">The response object.</param> 
    protected override void WriteFile(HttpResponseBase response) 
    { 
     // Convert the IList<T> to a datatable. 
     dataTable = list.ConvertToDatatable<T>(); 

     // Add the header and the content type required for this view. 
     response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", filename)); 
     response.ContentType = base.ContentType; 

     // Gets the current output stream. 
     var outputStream = response.OutputStream; 

     // Create a new memorystream. 
     using (var memoryStream = new MemoryStream()) 
     { 
      WriteDataTable(memoryStream); 
      outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); 
     } 
    } 

나는 이미 단위 테스트에서 다음을 시도했다 : :

 HttpContextBaseMock = new Mock<HttpContextBase>(); 
     HttpRequestMock = new Mock<HttpRequestBase>(); 
     HttpResponseMock = new Mock<HttpResponseBase>(); 
     HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object); 
     HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object); 

     var routes = new RouteCollection(); 
     var controller = new CsvActionResultController(); 
     controller.ControllerContext = new ControllerContext(HttpContextBaseMock.Object, new RouteData(), controller); 
     controller.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, new RouteData()), routes); 

     var result = controller.ExportToCSV(); 

그러나, 나는 그것이 작동하지 않는

는 여기에 테스트 할 방법입니다.

/// <summary> 
    ///  Start writing the file. 
    /// </summary> 
    /// <param name="response">The response object.</param> 
    protected override void WriteFile(HttpResponseBase response) 
    { 
     // Add the header and the content type required for this view. 
     response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", filename)); 
     response.ContentType = base.ContentType; 

     // Gets the current output stream. 
     var outputStream = response.OutputStream; 

     // Create a new memorystream. 
     using (var memoryStream = new MemoryStream()) 
     { 
      WriteDataTable(memoryStream); 
      outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); 
     } 
    } 

    #endregion Methods 

    #region Helper Methods 

    /// <summary> 
    ///  Writes a datatable to a given stream. 
    /// </summary> 
    /// <param name="stream">The stream to write to.</param> 
    private void WriteDataTable(Stream stream) 
    { 
     var streamWriter = new StreamWriter(stream, encoding); 

     // Write the header only if it's indicated to write. 
     if (includeRowHeader) 
     { WriteHeaderLine(streamWriter); } 

     // Move to the next line. 
     streamWriter.WriteLine(); 

     WriteDataLines(streamWriter); 

     streamWriter.Flush(); 
    } 

    /// <summary> 
    ///  Writes the header to a given stream. 
    /// </summary> 
    /// <param name="streamWriter">The stream to write to.</param> 
    private void WriteHeaderLine(StreamWriter streamWriter) 
    { 
     foreach (DataColumn dataColumn in dataTable.Columns) 
     { 
      WriteValue(streamWriter, dataColumn.ColumnName); 
     } 
    } 

    /// <summary> 
    ///  Writes the data lines to a given stream. 
    /// </summary> 
    /// <param name="streamWriter"><The stream to write to./param> 
    private void WriteDataLines(StreamWriter streamWriter) 
    { 
     // Loop over all the rows. 
     foreach (DataRow dataRow in dataTable.Rows) 
     { 
      // Loop over all the colums and write the value. 
      foreach (DataColumn dataColumn in dataTable.Columns) 
      { WriteValue(streamWriter, dataRow[dataColumn.ColumnName].ToString()); } 
      streamWriter.WriteLine(); 
     } 
    } 

    /// <summary> 
    ///  Write a specific value to a given stream. 
    /// </summary> 
    /// <param name="writer">The stream to write to.</param> 
    /// <param name="value">The value to write.</param> 
    private void WriteValue(StreamWriter writer, String value) 
    { 
     writer.Write(value); 
     writer.Write(delimeter); 
    } 

사람이 올바른 방향으로 날 포인트 :이 필요한 경우

, 여기에 전체 CsvActionResult의 (생성자없이) 소스입니까? 나는 조롱하는 것을 처음이다. 개인적으로 나는이 길을 갈 것

종류와 관련,

+0

정확한 예외는 무엇입니까? ExportToCSV() 액션은 어떻게 생겼습니까? 어떻게 WriteFile() 메서드를 호출합니까? – Spock

+0

글쎄, ExportToCSV가 "content-dispotion"을 사용하여 응답에 파일을 추가한다고 가정 해 봅시다. 나는 내 작업의 결과가 기록되는 전체 응답 스트림을 얻을 전체 응답 스트림 (그것에서 모든 속성)을 확인하기 위해 나에게 단위 테스트를하고 싶습니다. 그러나 나는 그것이 작동하도록하지 않습니다. 예 : Response.OutputStream은 항상 null입니다. Response.ContentStream은 null입니다. 내가 예를 들어 파일 이름, 콘텐츠 유형을 확인하고 싶은 , ... 종류의 안부 – Complexity

답변

-1

나는 그것을 해결하기 위해 관리했습니다,하지만 난 그게 좋은 방법 모르겠어요. 다른 질문으로 게시합니다.

모든 도움에 감사드립니다.

Kr,

+1

다른 질문은 어디에 있습니까? –

1

는 웹 응답은 특히 단위 테스트 시나리오에 해당하는 결과가 포함되도록합니다. 실제 웹 상호 작용 (예 : 올바른 값으로 채워진 모든 관련 속성 포함)을 사용하여 완전한 기능 테스트를 원하면 Acceptance Test/UI Test를 사용하여 파일 내보내기의 올바른 동작을 확인합니다.

HOWEVER 단위 테스트는 개별적으로 작업을 확인하고 구현 한 동작에 대한 빠른 피드백을 얻고 자 할 때 여전히 중요합니다. 이러한 Unit 테스트를 작성하는 방법은 Unit Testing 동안 ASP.NET 런타임 실행이 완전하지 않다는 점을 인식하여 약간 다릅니다.

그래서 당신에 관해서 예를 들어

코멘트 : Response.OutputStream는 항상 null입니다. Response.ContentStream은 null입니다. 단지 ControllerContext에 HttpContextBase, HttpResponseBase 밖으로 스텁 및 할당

참고는 등의 OutputStream, ContentStream, 응답 헤더로, 콘텐츠 형식은 단위 테스트 실행 중에 설정에 필요한 모든 특성을 가지고 것을 의미하지 않는다 문맥. Moq.Mock에서 제공하는 프록 싱/조롱/스텁 된 객체 (예 : HttpResponseMock)에서 작업 중이므로 정상적으로 ASP.NET 웹을 실행하는 동안 얻을 수있는 것과 같은 속성을 사용할 수 없습니다. 이것이 Unit Test 실행 컨텍스트에서 이러한 속성이 null을 반환하는 이유입니다.

thes 속성을 null을 반환하지 않도록 만들 수 있습니다. 일반적으로 다른 가상 속성과 마찬가지로 이러한 속성을 스텁 처리 할 수 ​​있습니다. 당신이 당신의 SUT에 모든 속성의 가짜 표현을 제공하기 위해 Moq.Mock을 사용할 수 있도록

httpResponseBaseStub.SetupGet(x => x.OutputStream).Returns(new Mock<Stream>().Object); 
    httpResponseBaseStub.SetupGet(x => x.ContentType).Returns("text/csv"); 

모든 속성은, HttpResponseBase의 방법은 가상입니다. AutoMocking과 같은 사전 조롱 기술은 불필요한 조롱을 제거하는 데 도움이되지만 기본적으로 모든 속성을 사용자에게 반환하도록 도와 줄 수 있습니다.하지만 그 세부 사항은 다루지 않을 것입니다.

하지만 위와 같은 stubbinh/faking 값이 Unit 테스트에서 확인해야하는 값을 실제로 추가하지 않기 때문에 사용자가 기대하는 바가 아닙니다. 그들은 가짜 값입니다.

는 그래서 다음 가장 좋은 방법 일 것입니다?

예상 값으로 호출 된 주요 HttpResponseBase 속성 중 일부를 확인하여 ExportToCSV의 동작을 확인하는 것이 더 좋을 것이라고 생각합니다. 예를 들어, 단위 테스트에서 아래와 같이 예상 값으로 호출 된 HttpResponse 속성을 확인하는 것으로 충분합니다.

[TestMethod] 
    public void CsvActionResultController_ExportToCSV_VerifyResponsePropertiesAreSetWithExpectedValues() 
    { 
     var sut = new HomeController(); 
     var httpResponseBaseMock = new Mock<HttpResponseBase>(); 

     //This would return a fake Output stream to you SUT 
     httpResponseBaseMock.Setup(x => x.OutputStream).Returns(new Mock<Stream>().Object); 
     var httpContextBaseStub = new Mock<HttpContextBase>(); 
     httpContextBaseStub.SetupGet(x => x.Response).Returns(httpResponseBaseMock.Object); 
     var controllerContextStub = new Mock<ControllerContext>(); 
     controllerContextStub.SetupGet(x => x.HttpContext).Returns(httpContextBaseStub.Object); 
     sut.ControllerContext = controllerContextStub.Object; 

     var result = sut.Index(); 

     httpResponseBaseMock.VerifySet(x => x.ContentType = "text/csv"); 
     httpResponseBaseMock.Verify(x => x.AddHeader("Content-Disposition", "attachment; filename=somefile.csv")); 

     //Any other verifications... 
    } 

또한 here도 유사한 검증을 사용한 또 다른 예이지만 약간 다른 접근법입니다. 내가 설명한 것과 ActionResult 유형을 테스트하여 FileContentResult를 사용하면 합리적으로 좋은 Unit Test를 얻을 수 있습니다.

Assert.IsInstanceOfType(actual, typeof(FileContentResult)); 

업데이트 (포함의 WriteFile 방법)

protected void WriteFile(HttpResponseBase response) 
    { 
     // Add the header and the content type required for this view. 
     string format = string.Format("attachment; filename={0}", "somefile.csv"); 
     response.AddHeader("Content-Disposition", format); 
     response.ContentType = "text/csv"; //if you use base.ContentType, 
     //please make sure this return the "text/csv" during test execution. 

     // Gets the current output stream. 
     var outputStream = response.OutputStream; 

     // Create a new memorystream. 
     using (var memoryStream = new MemoryStream()) 
     { 
      // WriteDataTable(memoryStream); 
      outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); 
     } 
    } 
+0

이봐, 답장을 보내 주셔서 감사합니다,하지만 불행하게도, 나는 오류 메시지가 나타납니다 않습니다. 메시지 : 한 번 이상 모의에 예상 호출 만이 수행되지 않았다 : X => x.ContentType = "텍스트/CSV" 위치 : httpResponseBaseMock.VerifySet (X => x.ContentType = "텍스트/csv "); 이유가 무엇이며 어떻게 해결할 수 있습니까? Kr, – Complexity

+0

원본 질문에 CsvActionResult (생성자 없음) 소스를 추가했습니다. – Complexity

+0

응용 프로그램을 실행할 때 base.ContentType의 값을 확인하십시오. 유닛 테스트 중에는 x.ContentType에 지정한 값과 일치해야합니다. 나의 예에서, SUT는 ContentType을 "text/csv"로 설정합니다. 예를 들어, response.ContentType = "text/csv"; 테스트 실행 중에는 base.ContentType이 "txt/csv"를 반환해야합니다. 내 버전의 WriteFile() 메서드로 코드를 업데이트했습니다. – Spock