2

Good day,

I am confused about unit testing the following:

1. MVC Controller:

[HttpGet]
public async Task<PartialViewResult> DoExternalCallsAsync()
{
    var model = new MyModel();

    await MyStaticLibrary.DoExternalWorkAsync(Server.MapPath("~\\") + "WorkSource.txt", model);

    return PartialView("_MyResults", model);
}

2. Static library:

public static async Task DoExternalWorkAsync(string sourcePath, MyModel model)
        {
            var externalCalls =
                System.IO.File.ReadAllLines(sourcePath)
                .Where(line => (!string.IsNullOrEmpty(line) && line.First() != '#'))
                .Select(p => DoExternalCall(p, model));

            await Task.WhenAll(externalCalls);


        }



 private static async Task DoExternalCall(string urlPath, MyModel model)
            {
                var result = await GetExternalApiResultAysnc(urlPath);

                // some code here...



                return;
            }

Basically, all that the controller does is call an external API, which does some work and returns a result or throws an error.

There are no interfaces or abstract classes with the external Api.

How do I go about unit testing this? N. B. I am not at liberty to change the design of the external Api.

Thanks,

agfc
  • 852
  • 1
  • 6
  • 13

2 Answers2

4

Using static classes or methods in your code makes that code hard to properly unit test. See Is static universally “evil” for unit testing and if so why does resharper recommend it?, Static class/method/property in unit test, stop it or not, When to use static classes in C#.

Wrap the static API call class into an instance class with an interface:

public interface IMyLibrary
{
    Task DoExternalWorkAsync();
}

public class MyStaticLibrary : IMyLibrary
{
    public async Task DoExternalWorkAsync(string sourcePath, MyModel model)
    {
        return await MyStaticLibrary.DoExternalWorkAsync(sourcePath, model);
    }
}

Then you can inject an instance into the controller's constructor.

Because the only thing that should be unit tested in this controller action method:

  • Does the code call the library with the proper arguments?
  • Does the method return the proper object?

During testing, you can inject a mocked IMyLibrary in the controller and verify that the controller correctly calls the mock, and you can verify that the result is a PartialViewResult containing whatever it should contain.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Thank you for the answer. I was once told that your design should only serve your main goals. Basically, you don't adjust your code to be "testable". Static library is there because there is no need for it not to be static. Also, how about that controller using Server.Mappath? – agfc May 06 '15 at 14:12
  • 1
    Well that's a broad discussion. I think you _do_ have to alter your code to make it testable, as those interests are conflicting: the most "dense" or "minimal" code usually isn't testable at all, while you _do_ want to test it. There are many patterns that help you make code more reusable and testable. I for one don't like static classes at all, especially when the methods they contain have side-effects (in this case, calling a web service, which should not happen during tests). Static classes are often used "so we don't have to use `new`", which is not an excuse for using static. – CodeCaster May 06 '15 at 14:20
  • As for `Server.MapPath()`, see [Unit testing for Server.MapPath](http://stackoverflow.com/questions/19563106/unit-testing-for-server-mappath). – CodeCaster May 06 '15 at 14:20
  • Hi. I have one [question in SO](http://stackoverflow.com/questions/30273517/best-practice-for-persisting-user-state-in-special-kind-of-asp-net-mvc-applicati). Could you please help me as it is very urgent. And I will really be helpful for your help. – İhsan İlicali May 16 '15 at 09:09
  • I'm with @CodeCaster on this. Testing your code _is_ one of your main goals. – Marjan Venema May 22 '15 at 19:30
0

The library is an implementation detail of performing some unit of work. It should be abstracted out completely. If you want to run integration tests, that's a different style of testing completely.

C Bauer
  • 5,003
  • 4
  • 33
  • 62