Home > other >  C# Pass controller method name dynamically and execute it
C# Pass controller method name dynamically and execute it

Time:09-16

I wrote the Unit Test code for my controller method, and it covers all the blocks.

Here is a relevant line in question.

[TestMethod()]

public void ManageAreasTest()
{
    //bunch of code
    var result = new AdminController().ManageAreas();
    //more code
}

This ManageAreas() line is pretty much the only thing that will change in the other unit test code logic (e.g., ManageSubAreas() controller method, which has exactly same logic as ManageAreas(), just returns a different view).

So, in order to avoid the copy-paste of unit test code and just change the method call, I want to pass controller method name dynamically and execute it.

For that, I created a new Class

public partial class ExecuteControllerMethodTest
{
    public string ActionName { get; set; }
    public string ControllerName { get; set; }
}

The only method that I'm aware of is RedirectToAction, so I created a method inside my Unit test class that does just that

private RedirectToRouteResult ExecuteControllerMethod(ExecuteControllerMethodTest model)
{
    return RedirectToAction(model.ActionName, model.ControllerName);
}

And in my Unit Test code I updated it like this

[TestMethod()]
    
    public void ManageAreasTest()
    {
        ExecuteControllerMethodTest model = new ExecuteControllerMethodTest();
        model.ControllerName = "Admin";
        model.ActionName = "ManageAreas";
        //bunch of code
        var result = ExecuteControllerMethod(model);
        //more code
    }

However, when I'm debugging the code, my breakpoint inside the ManageAreas() is not hit.

Overall, here is my question:

  • Am I using the right approach to achieve that goal?

  • And if so, what am I doing wrong?

Thank you very much in advance!

EDIT:

My plan is to create an extra method that would contain pretty much the entire code from ManageAreasTest(). The only thing that the UT method will do at this point is initiate the class variables and run the method itself.

[TestMethod()]

public void ManageAreasTest()
{
    ExecuteControllerMethodTest model = new ExecuteControllerMethodTest();
    model.ControllerName = "Admin";
    model.ActionName = "ManageAreas";
    
    RunAdminControllerUnitTestForMethod(model);
}

private void RunAdminControllerUnitTestForMethod(model)
{
    //Pretty much the entire code from ManageAreasTest()
    //Just instead of var result = new AdminController().ManageAreas();
    //It will be
    var result = ExecuteControllerMethod(model);
}

I think the better question will be: what are the other ways to call the controller method other than

var result = new AdminController().ManageAreas();

When the method name will be dynamic? Like, if I want to pass "ManageAreas", it will execute this method just like new AdminController().ManageAreas().

CodePudding user response:

Normally, when you have a bunch of repetitive code, the usual approach (in unit test or production code) is to extract a (private) method with the duplicated code, and only duplicate the calls. So your original method could be refactored into something like this:

[TestMethod()]
public void ManageAreasTest()
{
    //Setup part, just a sample idea
    var controller = this.BuildController();

    //Here goes the call itself, where you use the previously built controller and call the appropriate method each time
    var result = controller.ManageAreas();

    //Assert part, here goes the duplicated code too
    //Parameters will help separate each test case apart
    this.DoAsserts("ManageView");
}

And then we define auxiliar methods into the test class:

private AdminController BuildController()
{
    //Setup any other environment you need

    //Create the controller
    var controller = new AdminController();

    //Here you configure and initialize the controller, inject dependencies and the like

    return controller;
}

private void DoAsserts(string viewName)
{
    //Here go the bunch of assert and other post-action validations
    Assert.AreEqual(......);
    Assert.AreEqual(......);
    Assert.AreEqual(......);
}

Of course, that's just a small example, but the general idea is that. Other tests just duplicate the calls (one liner) and change the method under test and the required parameters.

CodePudding user response:

I managed to achieve my objective with the following line of code:

AdminController adm = new AdminController();
typeof(AdminController).GetMethod(model.ActionName).Invoke(adm,null);

That way, I'm passing the method name as a string, and once this code is executed, it goes straight to the controller method in question.

  • Related