I have the following method in my HomeController. The purpose is to split users based on IP address to test different versions of the home page:
[HttpGet]
public ActionResult Index()
{
var userIpAddress = GetUserIpAddress();
if (IsIpAddressOddOrEven(userIpAddress))
{
return RedirectToAction(HomePage);
}
return RedirectToAction(HomePageAlternative);
}
The GetUserIpAddress method:
private string GetUserIpAddress()
{
HttpContext context = System.Web.HttpContext.Current;
var ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(ipAddress))
{
string[] addresses = ipAddress.Split(',');
if (addresses.Length != 0)
{
return addresses[0];
}
}
return context.Request.ServerVariables["REMOTE_ADDR"];
}
I want to write a unit test to ensure that this works properly. However, every time the unit test runs it is just taking the IP address I currently have. I am struggling to work out how to mock the result of the 'GetUserIpAddress' method to return an odd or even string. My attempt so far:
[Test]
public void Test()
{
var controller = CreateMvcController<HomeController>();
var result = controller.Index();
controller.HttpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Returns("1");
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}
I got the error that the result of controller.HttpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Returns("1"); is not a string but a HttpResponseBase, and in any case I am not convinced I am going about this the right way. Please can someone point me in the right direction to do this? Thank you
I suggest you to move GetUserIpAddress method to a helper class which you can inject into HomeController. Therefore you can mock it while doing the unit tests.
Your HomeController will be like this
public HomeController(IUserIpAddressHelper userIpAddressHelper)
{
_userIpAddressHelper = userIpAddressHelper;
}
[HttpGet]
public ActionResult Index()
{
var userIpAddress = _userIpAddressHelper.GetUserIpAddress(System.Web.HttpContext.Current);
if (_userIpAddressHelper.IsIpAddressOddOrEven(userIpAddress))
{
return RedirectToAction(HomePage);
}
return RedirectToAction(HomePageAlternative);
}
So, you'll be able to mock UserIpAddressHelper and inject it when writing the test.
public void Test()
{
var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.0");
var controller = new HomeController(userIpAddressHelper);
var result = controller.Index();
Assert.IsInstanceOf<RedirectToRouteResult>(result);
var redirectToRouteResult = result as RedirectToRouteResult;
Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}
Related
I have a web api as below, which is being called from my angularjs UI.
public class ValuesController : Controller
{
private static string dynamoDbTable = string.Empty;
private readonly IDynamoDbManager<MyModel> _dynamoDbManager;
public ValuesController(IOptions<Dictionary<string, string>> appSettings, IDynamoDbManager<MyModel> dynamoDbManager)
{
var vals = appSettings.Value;
dynamoDbTable = vals["dynamoDbTable"];
_dynamoDbManager = dynamoDbManager;
}
[HttpGet("api/data")]
public async Task<IActionResult> GetAllData(string status, string role)
{
List<ScanCondition> conditions = new List<ScanCondition>();
conditions.Add(new ScanCondition("status", ScanOperator.Contains, status));
conditions.Add(new ScanCondition("role", ScanOperator.Contains, role));
List<MyModel> model = new List<MyModel>();
model = await _dynamoDbManager.GetAsync(conditions);
return Ok(model);
}
[HttpPost("api/save")]
public async Task<IActionResult> SaveData([FromBody] List<MyModel> listData, string input, string name, string type)
{
List<MyModel> model = new List<MyModel>();
foreach (var data in listData)
{
//populating data here and saving
await _dynamoDbManager.SaveAsync(data);
}
return Ok();
}
}
}
Now I want to write test cases for my API endpoints. I have not written any unit test case before so need input in that. I have read basic documentation of xunit here.
Here is my sample test method:
public class ValuesControllerTests
{
private Mock<IDynamoDbManager<MyModel>> _dbManager;
private ValuesController _valueController;
public ValuesControllerTests()
{
var mockRepository = new MockRepository(MockBehavior.Loose);
_dbManager = mockRepository.Create<IDynamoDbManager<MyModel>>();
var options = new OptionsWrapper<Dictionary<string, string>>(new Dictionary<string, string>()
{
{"dynamoDbTable", nameof(MyModel) }
});
_valueController = new ValuesController(options, _dbManager.Object);
}
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ }
};
//Here I am trying to use MOQ but this is not working fine
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var result = _valueController.GetAllData("new", "admin");
Assert.IsType<OkObjectResult>(result.Result);
}
}
}
Can I test my method without using moq, if so how can I do so? Because with moq I am not able to test.
Secondly above test case I am just checking if api method returns OK. What other test cases can I write for my get and save method.
I have also created a git project in case anyone wants to look at project: https://github.com/kj1981/xunit
Would appreciate if someone can help me in this.
--Added--
I also tried one test as below, but is this fine? May be my thinking is wrong, with the below test I thought it would fail but it does not.
[Fact]
public async Task GetAllData_Test()
{
var searchResult = new List<MyModel>()
{
new MyModel(){ Id ="1", Name = "Adam", Role = "User", Status ="old" },
new MyModel(){ Id ="2", Name = "Gary", Role = "Admin", Status ="new" }
};
_dbManager
.Setup(_ => _.GetAsync(It.IsAny<List<ScanCondition>>()))
.ReturnsAsync(searchResult);
var okResult = _valueController.GetAllData("fail", "none").Result as OkObjectResult; ;
var items = Assert.IsType<List<MyModel>>(okResult.Value);
Assert.Equal(2, items.Count);
}
Anyone with inputs ?
So I have a controller that is using HttpClient to call a webservice like so:
public class DemoController : Controller
{
HttpClient client;
string baseUrl = "http://localhost:90/webservice";
public DemoController()
{
client = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
}
// GET: DemoInfo
public async Task<ActionResult> Index()
{
HttpResponseMessage response = await client.GetAsync(baseUrl + "vehicle/menu/year");
string content = "";
MenuItems result = null;
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
}
return View("Index", result);
}
}
My unit test for this action is as follows:
[TestMethod]
public async Task Test_Index()
{
// Arrange
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
So obviously I would like to avoid making the web service call every time the test is run. Would I be on the right track in opting for an IoC container like Unity so that HttpClient would be injected into the controller? Is that overkill for what I'm trying to achieve? I'm aware that there is a lot of history with people struggling with properly mocking httpclient in there unit tests through this github issue. Any help would be greatly appreciated in giving some insight into how to write the controller to make a service call while still being testable.
All dependencies which makes tests slow should be abstracted.
Wrap HttpClient with an abstraction, which you can mock in your tests.
public interface IMyClient
{
Task<string> GetRawDataFrom(string url);
}
Then your controller will depend on that abstraction
public class DemoController : Controller
{
private readonly IMyClient _client;
private string _baseUrl = "http://localhost:90/webservice";
public DemoController(IMyClient client)
{
_client = client;
}
public async Task<ActionResult> Index()
{
var rawData = _client.GetRawDataFrom($"{_baseUrl}vehicle/menu/year");
using (var reader = new StringReader(rawData))
{
var result =
(MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(reader);
return View("Index", result);
}
}
}
Then in tests you can mock your abstraction to return expected data
public class FakeClient : IMyClient
{
public string RawData { get; set; }
public Task<string> GetRawDataFrom(string url)
{
return Task.FromResult(RawData);
}
}
[TestMethod]
public async Task Test_Index()
{
// Arrange
var fakeClient = new FakeClient
{
RawData = #"[
{ Name: "One", Path: "/one" },
{ Name: "Two", Path: "/two" }
]"
};
DemoController controller = new DemoController(fakeClient);
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult)result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
Actual implementation will use HttpClient
public class MyHttpClient : IMyClient
{
public Task<string> GetRawDataFrom(string url)
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
}
}
An alternative approach to testing HttpClient calls without service wrappers, mocks, or IoC containers is to use Flurl, a small wrapper library around HttpClient that provides (among other things) some robust testing features. [Disclaimer: I'm the author]
Here's what your controller would look like. There's a few ways to do this, but this approach uses string extension methods that abstract away the client entirely. (A single HttpClient instance per host is managed for you to prevent trouble.)
using Flurl.Http;
public class DemoController : Controller
{
string baseUrl = "http://localhost:90/webservice";
// GET: DemoInfo
public async Task<ActionResult> Index()
{
var content = await baseUrl
.AppendPathSegment("vehicle/menu/year")
.GetStringAsync();
var result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
return View("Index", result);
}
}
And the test:
using Flurl.Http;
[TestMethod]
public async Task Test_Index()
{
// fake & record all HTTP calls in the test subject
using (var httpTest = new HttpTest())
{
// Arrange
httpTest.RespondWith(200, "<xml>some fake response xml...</xml>");
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
}
Flurl.Http is available on NuGet.
I'm trying to create a custom IHttpActionResult type in web api 2 that will return content as HTML instead of json. What I'm struggling with is how to unit test an ApiController that returns my new ActionResult type. Many example showing how to unit test an ApiController tells you to cast it to OkNegotiatedContentResult and then read the content property off it but this doesn't seem to work in my case. When I debug the test, the code block in ExecuteAsync never seems to be called. Do I need to do this explicitly in my unit tests? Any help would be much appriciated
This is how my ActionResult looks like
public class HtmlActionResult : IHttpActionResult
{
String _html;
public HtmlActionResult(string html)
{
_html = html;
}
public Task<System.Net.Http.HttpResponseMessage> ExecuteAsync(System.Threading.CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(_html );
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/html");
return Task.FromResult(response);
}
}
This is my ApiController
public class HomeController : ApiController
{
public IHttpActionResult Get(string page)
{
return new HtmlActionResult("<html></html>");
}
}
And this is my test method
[TestMethod]
public async Task Get()
{
//Arrenge
HomeController controller = new HomeController();
//Act
IHttpActionResult result = controller.Get();
//Assert
//Assert.IsNotNull(result.Content);
}
Use Fluent Assertions:
IHttpActionResult result = await controller.Get()
HtmlActionResult htmlActionResult = result.Should()
.BeOfType<HtmlActionResult>()
.Which; // <-- the key feature
// or .And
what means you can nest assertions:
IHttpActionResult result = await controller.Get()
result.Should().BeOfType<HtmlActionResult>()
.Which.Content.Should().Be(expected);
Or just as #Spock suggested test things separately:
Get() returns IHttpActionResult which is actually HtmlActionResult
How HtmlActionResult works independently from Get()
You are not waiting for the controller action to complete - you should change your test to something like (this is untried):
public async Task Get()
{
// Arrange
HomeController controller = new HomeController();
// Act
IHttpActionResult result = await controller.Get();
// Assert
Assert.IsNotNull(result.Content);
}
Assuming that you're using WebAPI version 2, there is a really good guide on how to Unit Test Controllers on The ASP.NET Site.
I was in a similar scenario to you, and was a bit iffy about making my controller methods return Tasks instead of IHttpActionResults - which I believe are much cleaner.
I managed to adapt the code under the Testing Actions that Return HttpResponseMessage section of the above link to get my unit test working as expected.
Below is a simplified outline of my scenario:
public class XyzController : ApiController
{
private readonly IDbContext _dbContext;
public XyzController(IDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpGet]
[Route("this-is-optional")]
public IHttpActionResult Get(<optional-params-here>)
{
// Do work
...
// My data is an array of objects
return Ok(data);
}
}
[TestFixture]
public class XyzControllerTest
{
[Test]
public void Get_ReturnsSuccessfully()
{
// Arrange
IDbContext testContext = MockDbContext.Create();
...
// Populate textContext here
...
XyzController xyzController = new XyzController(testContext)
{
// These are required or exceptions will be thrown
Request = new HttpRequestMessage();
Configuration = new HttpConfiguration()
};
...
// Act
var response = xyzController.Get(<params-if-required>).ExecuteAsync(CancellationToken.None);
// Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsCompleted);
Assert.AreEqual(HttpStatusCode.OK, response.Result.StatusCode);
// Assertions on returned data
MyModel[] models;
Assert.IsTrue(response.Result.TryGetContentValue<MyModel[]>(out models));
Assert.AreEqual(5, model.Count());
Assert.AreEqual(1, model.First().Id);
...
}
}
Try this:
[TestMethod]
public void Get()
{
//Arrenge
var controller = new HomeController();
//Act
var result = controller.Get().Result as HtmlActionResult;
//Assert
Assert.IsNotNull(result);
}
Notice that your test can be void and you don't have to await your Get method, you can use.Result to run the task.
Also I am casting the result to HtmlActionResult, which will end up result to be Null if the result was a different ActionResult like OkResult, or NotFoundResult,...etc.
Hope that helps.
I have a controller called PostsController
public class PostsController : Controller
{
private const int PageSize = 8;
private readonly IPostsRepository repository;
public PostsController(IPostsRepository repository)
{
this.repository = repository;
}
public ViewResult Index(int pageNumber = 1)
{
var posts =
repository.All()
.Where(post => !post.Draft)
.OrderBy(post => post.PublishedAt);
var model =
posts.MapTo<PostViewModel>()
.ToPagedList(pageNumber, PageSize);
return View("Index", model);
}
public ActionResult Post(string slug)
{
var post =
repository.Find(slug);
if (post == null || post.Draft)
{
return HttpNotFound();
}
return View("Post", post.MapTo<PostViewModel>());
}
}
And a corresponding test fixture called PostsControllerTest
[TestFixture]
public class PostsControllerTest
{
private PostsController controller;
private Mock<IPostsRepository> repository;
[SetUp]
public void SetUp()
{
AutoMapperConfig.Configure();
repository = new Mock<IPostsRepository>();
controller = new PostsController(repository.Object);
}
[Test]
public void Index_ReturnsCorrectViewName()
{
var actual = controller.Index();
Assert.AreEqual(actual.ViewName, "Index");
}
[Test]
public void Index_ReturnsCorrectModel()
{
var result = controller.Index();
var actual = result.Model as PagedList<PostViewModel>;
Assert.NotNull(actual);
}
[Test]
public void Index_WithPageNumber_ReturnsCorrectViewName()
{
var actual = controller.Index(2);
Assert.AreEqual(actual.ViewName, "Index");
}
[Test]
public void Index_WithPageNumber_ReturnsCorrectModel()
{
var result = controller.Index(2);
var actual = result.Model as PagedList<PostViewModel>;
Assert.NotNull(actual);
}
[Test]
public void Post_ReturnsCorrectViewName()
{
repository.Setup(repo => repo.Find("abc"))
.Returns(new Post());
var actual = controller.Post("abc") as ViewResult;
Assert.NotNull(actual);
Assert.AreEqual(actual.ViewName, "Post");
}
[Test]
public void Post_ThatIsDraft_ReturnsNotFound()
{
var post = new Post { Draft = true };
repository.Setup(repo => repo.Find("abc"))
.Returns(post);
var actual = controller.Post("abc");
Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
}
[Test]
public void Post_ThatDoesNotExist_ReturnNotFound()
{
var actual = controller.Post("abc");
Assert.IsAssignableFrom<HttpNotFoundResult>(actual);
}
[Test]
public void Post_ReturnsCorrectModel()
{
var post = new Post
{
Slug = "continuing-to-an-outer-loop",
Title = "Continuing to an outer loop",
Summary = "When you have a nested loop, sometimes",
Content = "When you have a nested loop, sometimes",
PublishedAt = DateTime.Now.AddDays(7),
Tags = new Collection<Tag> { new Tag { Name = "Programming" } }
};
repository.Setup(repo => repo.Find("continuing-to-an-outer-loop"))
.Returns(post);
var viewResult = (ViewResult)controller.Post("continuing-to-an-outer-loop");
var actual = viewResult.Model as PostViewModel;
Assert.NotNull(actual);
Assert.AreEqual(actual.Slug, post.Slug);
Assert.AreEqual(actual.Title, post.Title);
Assert.AreEqual(actual.Summary, post.Summary);
Assert.AreEqual(actual.Content, post.Content);
Assert.AreEqual(actual.PublishedAt, post.PublishedAt);
Assert.AreEqual(actual.Tags, post.Tags);
}
}
I learned to mock the repository in this way by observing how other projects arranged their tests. One particular example of this approach (from which I learned) can be found here. Personally I have found this approach to be somewhat laborious. Furthermore, since adopting this approach I stumbled upon this post that states that you should not mock your repository but fake it instead.
Now I am conflicted. Should I proceed to mock my repository of fake it instead?
I kindly ask that you include code examples that show how to fake and seed the repository in this case as I am not sure how to do so.
To pick a side, I'd say mocking is just fine and is less work than faking.
But to voice an opinion - without trying to be awkward - I'd say... neither.
Consider how much value is being added by a test which goes through your Index action without hitting a real repository - the vast majority of the code you're testing is in Linq and AutoMapper, both of which have already been heavily tested by other people.
I would recommended writing Integration tests which run through your controller actions, through your real repositories, hit your database and come back out the other side. That adds value and gives you some real assurance that your system (i.e. the bits you've written) actually works.
Finally, Jimmy Bogard (of AutoMapper fame) has a good blog entry about testing repositories here.
Here's a sample of one of my unit test classes (pared down to the basics). In the controller, when the Index() action method is invoked, a call to GetByID(1234) always results in a newed up instance of a Ticket object. The object exists, but all of its properties are null, even though I've set them in my fake object. Any ideas as to why?
I'm using Moq.
Unit test
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>();
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
Mock<HttpContextBase> context = FakeHttpContext();
context.Setup(ctx => ctx.Session[SessionKeys.TokenData.ToString()]).Returns(Constants.TOKENDATA_SUBMITTER);
MockTicketRepository.Setup(x => x.GetById(It.IsAny<int>())).Returns(Constants.CLIENT_TICKET);
//ACT
var result = GetController(context.Object).Index(Constants.TICKET_ID);
var model = ((ViewResult)result).Model;
//ASSERT
Assert.IsInstanceOfType(model, typeof(TicketModel), "ViewModel should have been an instance of TicketModel.");
}
private TicketController GetController(HttpContextBase context)
{
var controller = new TicketController(MockTicketRepository.Object);
controller.ControllerContext = GetControllerContext(context, controller);
return controller;
}
}
Constants.CLIENT_TICKET
public static Ticket CLIENT_TICKET
{
get
{
var ticket = new Ticket
{
CategoryID = 1,
CreatedByUserId = 4
};
ticket.Clients.Add(new Client { ShortName = "Test Client 1"});
ticket.Clients.Add(new Client { ShortName = "Test Client 2" });
ticket.User = new User {FirstName = "First", LastName = "Last"};
return ticket;
}
}
Controller
private readonly ITicketRepository _ticketRepository;
public TicketController(ITicketRepository ticketRepository)
{
_ticketRepository = ticketRepository;
}
public ActionResult Index(int id)
{
var ticket = _ticketRepository.GetById(id);
// etc...
}
Could you show the controller code under test? It could be related to how you have set up the mocked context but it's hard to tell without seeing the controller code.
Also, if you add MockBehavior.Strict when you create the mock, it will bomb out if the invocation doesn't have a corresponding expectation:
protected Mock<ITicketRepository> MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
UPDATE
I've tried to strip everything back so that the test is as simple as possible to try and isolate the issue. Here's what I have come up with:
[TestClass]
public class TicketControllerTests : ControllerTestBase
{
protected Mock<ITicketRepository> MockTicketRepository;
[TestMethod]
public void IndexActionModelIsTypeOfTicketModel()
{
//ARRANGE
MockTicketRepository = new Mock<ITicketRepository>(MockBehavior.Strict);
MockTicketRepository.Setup(x => x.GetById(Constants.TICKET_ID)).Returns(Constants.CLIENT_TICKET);
var controller = new TicketController(MockTicketRepository.Object);
//ACT - try to keep ACT as lean as possible, ideally just the method call you're testing
var result = controller.Index(Constants.TICKET_ID);
//ASSERT
var model = ((ViewResult)result).ViewData.Model;
Assert.That(model, Is.InstanceOfType<TicketModel>(), "ViewModel should have been an instance of TicketModel.")
}
}