I have class which creates instance of HttpClient and call some method and returns response in async mode.
For ReadAsAsync, I have used nuget package "System.Net.Http.Formatting".
Source Code:
public class MyClass
{
readonly HttpClient client = new HttpClient();
public MyClass()
{
string myUrl = ConfigurationManager.AppSettings["MyWebAPI"];
client.BaseAddress = new Uri(myUrl);
}
public async Task<List<YourClass>> GetYourClass()
{
var filters = "string";
HttpResponseMessage response = await client.GetAsync(filters).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var notes = await response.Content.ReadAsAsync<List<YourClass>>();
return notes;
}
return null;
}
}
public class YourClass
{
private string Address { get; set; }
public YourClass(string address)
{
Address = address;
}
}
Unit Test:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
using (ShimsContext.Create())
{
MyClass obj = new MyClass();
ShimHttpClient shimHttpClient = new ShimHttpClient();
ShimHttpClient.Constructor = (t) =>
{
shimHttpClient = new ShimHttpClient();
shimHttpClient.GetAsyncString = (a) =>
{
return new System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>(function1);
};
};
var returnVal = obj.GetYourClass();
}
}
private System.Net.Http.HttpResponseMessage function1()
{
return new System.Net.Http.HttpResponseMessage();
}
}
We can't change the source code. but need to unit test GetAsync and ReadAsAync call through dummy call test.
Declare that your test to run async and await the response from the source class
[TestMethod]
public async Task TestMethod1()
{
using (ShimsContext.Create())
{
MyClass obj = new MyClass();
ShimHttpClient shimHttpClient = new ShimHttpClient();
ShimHttpClient.Constructor = (t) =>
{
shimHttpClient = new ShimHttpClient();
await shimHttpClient.GetAsyncString = (a) =>
{
return new System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>(function1);
};
};
var returnVal = await obj.GetYourClass();
Assert.IsNotNull(returnVal);
//make more assertations on the returnVal
}
}
Related
[Test]
public async Task TestSubscriber()
{
var subscriber = new Mock<SubscriberServiceApiClient>();
subscriber.Setup(x =>
x.PullAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CallSettings>()))
.ReturnsAsync(new PullResponse
{
ReceivedMessages =
{
new ReceivedMessage()
{
Message = new PubsubMessage
{
MessageId = "1",
PublishTime = DateTime.UtcNow.ToTimestamp(),
OrderingKey = "1"
}
}
}
});
var mySubscriber = new MySubscriber(subscriber.Object);
await mySubscriber.PullMessage(new CancellationToken());
}
public class MySubscriber
{
private SubscriberServiceApiClient _subscriberServiceApiClient;
...
public MySubscriber(SubscriberServiceApiClient subscriberServiceApiClient)
{
...
_subscriberServiceApiClient = subscriberServiceApiClient;
}
public async Task PullMessage(CancellationToken cancellationToken)
{
var messages = await _subscriberServiceApiClient.PullAsync("mysubscription", 10);
//always null
}
}
I don't see what I'm doing wrong here.
Is it possible to mock SubscriberServiceApiClient?
I have 4 methods:
2 x PUT and 2 x POST. With one complex parameter (POCO class) or three simple parameters:
public class PocoRequest
{
[JsonPropertyName("arg01")]
public string Arg01 { get; set; }
[JsonPropertyName("arg02")]
public Guid Arg02 { get; set; }
[JsonPropertyName("arg03")]
public string Arg03 { get; set; }
}
[ApiController]
[Route("api/[controller]")]
public class PostPlainController : ControllerBase
{
[HttpPost(Name = "PostPlainRequest")]
public IActionResult PutPlainRequest(string arg01, Guid arg02, string arg03)
{
var middle = arg02.ToString();
var first = middle.ToLower();
var second = middle.ToUpper();
return arg01 == first && arg03 == second ? Ok() : NotFound();
}
}
[ApiController]
[Route("api/[controller]")]
public class PostPocoController : ControllerBase
{
[HttpPost(Name = "PostPocoRequest")]
public IActionResult PutPocoRequest(PocoRequest request)
{
var middle = request.Arg02.ToString();
var first = middle.ToLower();
var second = middle.ToUpper();
return request.Arg01 == first && request.Arg03 == second ? Ok() : NotFound();
}
}
[ApiController]
[Route("api/[controller]")]
public class PutPlainController : ControllerBase
{
[HttpPut(Name = "PutPlainRequest")]
public IActionResult PutPlainRequest(string arg01, Guid arg02, string arg03)
{
var middle = arg02.ToString();
var first = middle.ToLower();
var second = middle.ToUpper();
return arg01 == first && arg03 == second ? Ok() : NotFound();
}
}
[ApiController]
[Route("api/[controller]")]
public class PutPocoController : ControllerBase
{
[HttpPut(Name = "PutPocoRequest")]
public IActionResult PutPocoRequest(PocoRequest request)
{
var middle = request.Arg02.ToString();
var first = middle.ToLower();
var second = middle.ToUpper();
return request.Arg01 == first && request.Arg03 == second ? Ok() : NotFound();
}
}
And tests:
public class PostUnitTest
{
[Fact]
public void PostPlainPositiveTest()
{
PostPlainPositiveTestAsync().Wait();
}
private async Task PostPlainPositiveTestAsync()
{
var url = "https://localhost:7095/api/PostPlain";
using var apiClient = new HttpClient();
var str = "0a4b9957-4505-45df-b593-47d365b5d25a";
var value = new Dictionary<string, string>
{
{ "arg01", str.ToLower() },
{ "arg02", Guid.Parse(str).ToString() },
{ "arg03", str.ToUpper() }
};
var content = new FormUrlEncodedContent(value);
var res = await apiClient.PostAsync(url, content);
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
}
[Fact]
public void PostPlainNegativeTest()
{
PostPlainNegativeTestAsync().Wait();
}
private async Task PostPlainNegativeTestAsync()
{
var url = "https://localhost:7095/api/PostPlain";
using var apiClient = new HttpClient();
var str = "0a4b9957-4505-45df-b593-47d365b5d25a";
var value = new Dictionary<string, string>
{
{ "arg01", str.ToUpper() },
{ "arg02", Guid.Parse(str).ToString() },
{ "arg03", str.ToUpper() }
};
var content = new FormUrlEncodedContent(value);
var res = await apiClient.PostAsync(url, content);
Assert.Equal(HttpStatusCode.NotFound, res.StatusCode);
}
[Fact]
public void PostPocoPositiveTest()
{
PostPocoPositiveTestAsync().Wait();
}
private async Task PostPocoPositiveTestAsync()
{
var url = "https://localhost:7095/api/PostPoco";
using var apiClient = new HttpClient();
var str = "0a4b9957-4505-45df-b593-47d365b5d25a";
var value = new PocoRequest
{
Arg01 = str.ToLower(),
Arg02 = Guid.Parse(str),
Arg03 = str.ToUpper()
};
var content = JsonContent.Create(value);
var res = await apiClient.PostAsync(url, content);
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
}
[Fact]
public void PostPocoNegativeTest()
{
PostPocoNegativeTestAsync().Wait();
}
private async Task PostPocoNegativeTestAsync()
{
var url = "https://localhost:7095/api/PostPoco";
using var apiClient = new HttpClient();
var str = "0a4b9957-4505-45df-b593-47d365b5d25a";
var value = new PocoRequest
{
Arg01 = str.ToUpper(),
Arg02 = Guid.Parse(str),
Arg03 = str.ToUpper()
};
var content = JsonContent.Create(value);
var res = await apiClient.PostAsync(url, content);
Assert.Equal(HttpStatusCode.NotFound, res.StatusCode);
}
}
(PutUnitTest - tests PUT actions - looks similar).
Api with single complex parameter works perfect (from Swagger and XUnit runner).
But I don't know how to consume (call) PUT/POST with multiple parameters from XUnit - Swagger works as expected.
I am using recommended HttpClient.
I have existing PUT/POST actions with multiple parameters. Migration to single request class is possible but painfull.
There is nothing wrong with client (consuming) code.
There is problem with controllers.
Plain controllers ie:
(string arg01, Guid arg02, string arg03)
have to be changed to:
([FromForm] string arg01, [FromForm] Guid arg02, [FromForm] string arg03)
I want to make unit tests in my app. I use NUNIT, and the following libraries :
Autofac.Extras.Moq
AutoFixture
I followed this samples but it doesn't work :
http://makanda.io/unit-testing-xamarin-forms-view-model/
Mock a method of class under test with Moq & AutoMock
Here is my test :
private Fixture _fixture;
[SetUp]
public void Setup()
{
_fixture = new Fixture();
}
[Test]
public void Login_Success()
{
using (var mock = AutoMock.GetLoose())
{
var infosLogin = _fixture.Create<LoginRequest>();
var loginResponse = _fixture.Create<LoginResponse>();
var userService = mock.Mock<IUserService>();
userService
.Setup(user => user.Login(infosLogin))
.Returns(Task.FromResult(loginResponse));
var viewModel = new MainPageViewModel(new ContentPage(), userService.Object);
viewModel.Login = infosLogin.Username;
viewModel.Password = infosLogin.Password;
viewModel.LoginCommand.Execute(null);
}
}
My view
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel(this, new UserServiceImpl());
}
My ViewModel
public MainPageViewModel(Page page, IUserService userService)
{
_page = page;
_userService = userService;
Login = "";
Password = "";
}
public Command LoginCommand
{
get
{
return new Command(async () =>
{
Console.WriteLine("step 1...");
if (!string.IsNullOrEmpty(Login) && !string.IsNullOrEmpty(Password))
{
Console.WriteLine("step 2...");
var infos = new LoginRequest() { Username = "Wyllis", Password = "test" };
LoginResponse response = await _userService.Login(infos);
Console.WriteLine("step 3...");
Console.WriteLine(response);
Age = response.Age; // Got error : Object reference not set to an instance of an object
}
});
}
}
LoginResponse model
public class LoginResponse
{
public string Username { get; set; }
public string Age { get; set; }
}
Age = response.Age;, I Got error : Object reference not set to an instance of an object, can you explain what is wrong ?
I found the solution, new LoginRequest(...) from my viewmodel and my tests are same BUT the hashcode (memory address) is different, so my test fail because my param is not the same object with the same memory than viewmodel
here is my new test and my view model
ViewModel
public Command LoginCommand
{
get
{
return new Command(async () =>
{
if (!string.IsNullOrEmpty(Login) && !string.IsNullOrEmpty(Password))
{
var infos = new LoginRequest() { Username = Login, Password = Password };
LoginResponse response = await _userService.Login(infos);
Age = response.Age;
}
});
}
}
Test
private Mock<IUserService> _userService;
private MainPageViewModel _viewModel;
private Fixture _fixture;
[SetUp]
public void Setup()
{
using (var mock = AutoMock.GetLoose())
{
_userService = mock.Mock<IUserService>();
}
_fixture = new Fixture();
}
private MainPageViewModel CreateInstance()
{
return new MainPageViewModel(_userService.Object);
}
[Test]
public void Test_Constructor()
{
using (var mock = AutoMock.GetLoose())
{
var viewModel = mock.Create<MainPageViewModel>();
viewModel.Login.Equals("");
viewModel.Password.Equals("");
}
}
[Test]
public void Login_Success()
{
var user = _fixture.Create<LoginResponse>();
_viewModel = CreateInstance();
_viewModel.Login = "test";
_viewModel.Password = "test";
_userService
.Setup(s => s.Login(It.IsAny<LoginRequest>()))
.Returns(Task.FromResult(user))
.Verifiable();
_viewModel.LoginCommand.Execute(null);
_userService.Verify();
Assert.AreEqual(_viewModel.Age, user.Age);
}
I am trying to write a unit test using Xunit and moq that will return a data from the client.GetAll().
At the moment when I debug the client.GetAll just return null.
This is my controller class
private readonly IClient _client;
public EventsController(IClient client)
{
_client = client;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<EventDTO>>> List()
{
var values = await _client.GetAll();
return Ok(values);
}
I am using my Client code implement. I am not sure if the IOptions is causing the issue
public class Client: IClient
{
public RestClient RestClient { get; set; }
public IOptions<MySettings>Settings { get; set; }
public Client(IOptions<MySettings>options)
{
Settings = options;
RestClient = new RestClient(options.Value.BaseUrl);
}
public async Task<List<EventDTO>> GetAll()
{
var request = new RestRequest(Settings.Value.GetAll, Method.GET);
var content = await RestClient.GetAsync<RootObject>(request);
var data = content.Events.SelectMany(con =>
con.Geometries.Select(geo =>
new EventDTO
{
Title = con.Title,
Id = con.Sources.FirstOrDefault()?.Id,
CategoriesTitle = con.Categories.FirstOrDefault()?.Title,
DateTime = geo.Date
})
).ToList();
return data;
}
This is my test class. I noticed that when i debug it does not go into the _client.GetAll() method.
Is this because I dont IOptionsoptions setup correctly?
public class EventsControllerTests
{
private readonly EventsController _controller;
private readonly Mock<IClient> _clientService;
public EventsControllerTests()
{
_clientService = new Mock<IClient>();
_controller = new EventsController(_clientService.Object)
{
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext()
}
};
}
[Fact]
public async Task Should_return_events_from_configured_service()
{
var response = new RootObject();
{
new Event()
{
Id = "sadsadsa"
};
};
var s = _clientService.Setup(x => x.Get(It.IsAny<string>())).ReturnsAsync(response);
var controllerResponse = await _controller.List();
var responseBody = controllerResponse.Value as IEnumerable<EventDTO>;
}
Thanks for the help.
You should test your GetAll method separately from the controller. In the controller you can just mock your client.
public class EventsControllerTests
{
private readonly Mock<IClient> _clientMock;
private readonly EventsController _testeeController;
public EventsControllerTests()
{
_clientMock = new Mock<IClient>();
_testeeController = new EventsController(_clientMock.Object);
}
[Fact]
public async Task ListTest()
{
var dtoList = new List<EventDto>{new EventDto(), new EventDto()};
_clientMock.Setup(c => c.GetAll()).ReturnsAsync(dtoList);
var response = await _testeeController.List();
var jsonResult = (JsonResult) response.Result;
var dtoListFromResponse = (IEnumerable<EventDto>) jsonResult.Value;
// compare dtoListFromResponse with dtoList
}
}
How I can add test cookie to request so I can test my code from Unit test. Consider a code like this:
public ActionResult Dashboard()
{
if (Request.Cookies["usercookie"] == null)
{
return RedirectToAction("Index");
}
return View();
}
I mock everything, but I don't know how I can add something to cookie so this line Request.Cookies["usercookie"] not return null. As now it is null and returning me this error:
{"Object reference not set to an instance of an object."}
This is one of my unit test methods:
[TestMethod]
[TestCategory("Unit")]
public void Login_ShouldValidateUserAndLoginSuccessfully()
{
using (var kernel = new NSubstituteMockingKernel())
{
// Setup the dependency incjection
kernel.Load(new EntityFrameworkTestingNSubstituteModule());
// Create test request data
var request = new LogInRequest { UserName = "test", Password = "test" };
var fakeResponseHandler = new FakeResponseHandler();
fakeResponseHandler.AddFakeResponse(new Uri("http://localhost/test"), new HttpResponseMessage(HttpStatusCode.OK));
ConfigurationManager.AppSettings["SearchApiBaseUrl"] = "http://test/internal";
var server = new HttpServer(new HttpConfiguration(), fakeResponseHandler);
var httpClient = new HttpClient(server);
var fakeCookieManager = new FakeCookieManager();
var authenticationService = Substitute.For<IAuthenticationService>();
var newUser = Fake.GetNewUser(1);
var newUserClaim = Fake.GetNewUserClaim(1, newUser.Id, "http://test/identity/claims/loans");
authenticationService.GetUserByEmailPasswordAsync(request.UserName, request.Password).Returns(newUser);
authenticationService.GetUserClaimByEmailAndPasswordAsync(request.UserName, request.Password).Returns(newUserClaim);
var controller = new HomeController(httpClient, fakeCookieManager, null, authenticationService);
Fake.SetFakeAuthenticatedControllerContext(controller);
controller.HttpContext.Session["ReturnUrl"] = "/search";
var result = controller.Login(request);
Assert.IsNotNull(result);
}
}
This is a class in Fake for Httpcontext:
public static HttpContextBase InitialiseFakeHttpContext(string url = "")
{
var HttpContextSub = Substitute.For<HttpContextBase>();
var RequestSub = Substitute.For<HttpRequestBase>();
var ResponseSub = Substitute.For<HttpResponseBase>();
var serverUtilitySub = Substitute.For<HttpServerUtilityBase>();
var itemsSub = Substitute.For<IDictionary>();
HttpContextSub.Request.Returns(RequestSub);
HttpContextSub.Response.Returns(ResponseSub);
HttpContextSub.Server.Returns(serverUtilitySub);
var cookie = Substitute.For<HttpResponseBase>();
HttpContextSub.Response.Returns(cookie);
return HttpContextSub;
}
Here is an example unit test where a cookie is set on the request.
Used NSubstitute framework to mock the http context and then setup the request cookies property. Applied the mocked http context to the controller context to simulate a request.
[TestClass]
public class MyControllerTests {
[TestMethod]
public void Request_Cookies_Should_Not_Be_Null() {
//Arrange
var cookies = new HttpCookieCollection();
cookies.Add(new HttpCookie("usercookie"));
var mockHttpContext = Substitute.For<HttpContextBase>();
mockHttpContext.Request.Cookies.Returns(cookies);
var sut = new MyController();
sut.ControllerContext = new ControllerContext {
Controller = sut,
HttpContext = mockHttpContext
};
//Act
var result = sut.Dashboard() as ViewResult;
//Assert
Assert.IsNotNull(result);
}
public class MyController : Controller {
public ActionResult Dashboard() {
if (Request.Cookies["usercookie"] == null) {
return RedirectToAction("Index");
}
return View();
}
}
}
Update:
Here is an updated version of the test using a manually created mocked HttpContext.
[TestClass]
public class MyControllerTests {
[TestMethod]
public void Request_Cookies_Should_Not_Be_Null() {
//Arrange
var cookies = new HttpCookieCollection();
cookies.Add(new HttpCookie("usercookie"));
var mockHttpContext = new MockHttpContext(cookies);
var sut = new MyController();
sut.ControllerContext = new ControllerContext {
Controller = sut,
HttpContext = mockHttpContext
};
//Act
var result = sut.Dashboard() as ViewResult;
//Assert
Assert.IsNotNull(result);
}
public class MyController : Controller {
public ActionResult Dashboard() {
if (Request.Cookies["usercookie"] == null) {
return RedirectToAction("Index");
}
return View();
}
}
private class MockHttpContext : HttpContextBase {
private readonly MockRequest request;
public MockHttpContext(HttpCookieCollection cookies) {
this.request = new MockRequest(cookies);
}
public override HttpRequestBase Request {
get {
return request;
}
}
public class MockRequest : HttpRequestBase {
private readonly HttpCookieCollection cookies;
public MockRequest(HttpCookieCollection cookies) {
this.cookies = cookies;
}
public override HttpCookieCollection Cookies {
get {
return cookies;
}
}
}
}
}
This is the same thing using MOQ framework
MockContext class used to mock the HTTP context
public class MockContext
{
public Mock<RequestContext> RoutingRequestContext { get; private set; }
public Mock<HttpContextBase> Http { get; private set; }
public Mock<HttpServerUtilityBase> Server { get; private set; }
public Mock<HttpResponseBase> Response { get; private set; }
public Mock<HttpRequestBase> Request { get; private set; }
public Mock<HttpSessionStateBase> Session { get; private set; }
public Mock<ActionExecutingContext> ActionExecuting { get; private set; }
public HttpCookieCollection Cookies { get; private set; }
public MockContext()
{
this.RoutingRequestContext = new Mock<RequestContext>(MockBehavior.Loose);
this.ActionExecuting = new Mock<ActionExecutingContext>(MockBehavior.Loose);
this.Http = new Mock<HttpContextBase>(MockBehavior.Loose);
this.Server = new Mock<HttpServerUtilityBase>(MockBehavior.Loose);
this.Response = new Mock<HttpResponseBase>(MockBehavior.Loose);
this.Request = new Mock<HttpRequestBase>(MockBehavior.Loose);
this.Session = new Mock<HttpSessionStateBase>(MockBehavior.Loose);
this.Cookies = new HttpCookieCollection();
this.RoutingRequestContext.SetupGet (c => c.HttpContext).Returns(this.Http.Object);
this.ActionExecuting.SetupGet (c => c.HttpContext).Returns(this.Http.Object);
this.Http.SetupGet (c => c.Request).Returns(this.Request.Object);
this.Http.SetupGet (c => c.Response).Returns(this.Response.Object);
this.Http.SetupGet (c => c.Server).Returns(this.Server.Object);
this.Http.SetupGet (c => c.Session).Returns(this.Session.Object);
this.Request.Setup (c => c.Cookies).Returns(Cookies);
}
}
and this is the test case
public void IndexTest()
{
// arrange
MockContext mockContext = new MockContext();
#region creating cookie
HttpCookie cookie = new HttpCookie(Constant.COOKIE_ADMIN_USER_INFO,
Config.DefaultCountryID.ToString());
cookie.Values.Add(Constant.COOKIE_ADMIN_VALUE_COUNTRY_ID,
Config.DefaultCountryID.ToString());
cookie.Values.Add(Constant.COOKIE_ADMIN_VALUE_LANGUAGE_ID,
Config.DefaultLanguageID.ToString());
mockContext.Cookies.Add(cookie);
#endregion
#region Creating controller
ControllerContext controllerContex = new ControllerContext()
{
HttpContext = mockContext.Http.Object
};
HomeController controller = new HomeController()
{
ControllerContext = controllerContex
};
#endregion
// act
var output = (ViewResult)controller.Index();
var result = output.ViewData;
// assert
result.ShouldNotBeNull();
}