1

I have a controller that need to test. It has a method with the function inside.

public ActionResult GetZZ()
    {            
        ApplyResponseHeaders();
        var result = new MediaJsonResult();
        using (var str = new StreamReader(Request.InputStream))
            {
                string inputData = str.ReadToEnd();
                MyFunction(inputData, result);
            }
        return Json(result);
    }

I just want to test the function MyFunction. This function is private. How can I do this. Test the entire method is not necessary, because the problems in the appointment of its values ​​in Request.InputStream

3
  • Normally we don't required to test private method, do you have any specific reason to do so? Commented Aug 19, 2013 at 11:30
  • the reason is that I can not enter a value in Request.InputStream, so it was decided to do so and to test only this method Commented Aug 19, 2013 at 11:35
  • What does MyFunction() do? Should it be encapsulated in its own type, and invoked by your controller, or is it something that is fairly specific to your controller, and depends on certain fields in your controller? Commented Aug 19, 2013 at 18:19

3 Answers 3

1

Don't try to test private methods ever. These methods are not part of public API and cannot be invoked by caller. Your goal is to satisfy requirements for public API. It really doesn't matter if private method works as expected or not. From caller's point of view this method does not exist and does not have any value.

Instead you should test functionality, which is available via public API, GetZZ() method in your case. But you should mock external dependencies in order to test your controller in isolation with any test data you want.

So, here you have two options. First one is mocking HttpRequest which your controller depends on, and providing test data for input stream (you will have to do lot of work):

var httpRequest = new Mock<HttpRequestBase>();
var stream = new MemoryStream(Encoding.Default.GetBytes("Hello world"));
httpRequest.Setup(r => r.InputStream).Returns(stream);

var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Request).Returns(httpRequest.Object);

var controller = new HomeController();
var routeData = new RouteData();
controller.ControllerContext = // set mocked context
     new ControllerContext(httpContext.Object, routeData, controller);

var result = (JsonResult)controller.GetZZ();
Assert.That(result.Data, Is.EqualTo(42)); // your assertions here

Another option - hiding this environment-related stuff under some abstraction, which can be easily mocked (of course, you should use better names):

public interface IFoo
{
    string Bar();
}

This is a implementation, which uses current context request to get input data:

public class Foo : IFoo
{
   public string Bar()
   {
      using (var str = new StreamReader(HttpContext.Current.Request.InputStream))
      {
          string inputData = str.ReadToEnd();
          return inputData;
      }
   }
}

Make controller depend on this abstraction:

public class HomeController : Controller
{
    private readonly IFoo _foo;

    public HomeController(IFoo foo) // inject dependency
    {
        _foo = foo;
    }

    public ActionResult GetZZ()
    {
        ApplyResponseHeaders();
        var result = new JsonResult();            
        MyFunction(_foo.Bar(), result); // use dependency
        return result;
    }
}

Now you can mock it without any problems:

var foo = new Mock<IFoo>();
foo.Setup(f => f.Bar()).Returns("Hello, TDD");
var controller = new HomeController(foo.Object);
var result = (JsonResult)controller.GetZZ();
Assert.That(result.Data, Is.EqualTo(42));
Sign up to request clarification or add additional context in comments.

Comments

0

One easy way is to make the method public. If you can't (or don't want to), you could make the method protected instead of private, then subclass your controller in your test assembly and test it through the derived type.

Something like so:

public class TesterController : YourController
{
    public new ActionResult MyFunction(string inputData, MediaJsonResult result)
    {
        return base.MyFunction(inputData, result);
    }
}

Comments

0

You can mark the methods as internal instead of private and then add a InternalsVisibleTo("Path.To.Test.Project") in AssemblyInfo.cs of your controllers.

Not 100% sure agree about NEVER EVER test a private method in your code. Like most things, sometimes it makes sense and being pragmatic is often better then being dogmatic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.