How to mock RestSharp portable library in Unit Test - c#

I would like to mockup the RestClient class for test purposes
public class DataServices : IDataServices
{
private readonly IRestClient _restClient;
public DataServices(IRestClient restClient)
{
_restClient = restClient;
}
public async Task<User> GetUserByUserName(string userName)
{
User user = null;
// create a new request
var restRequest = new RestRequest("User", Method.GET);
// create REST parameters
restRequest.AddParameter("userName", userName, ParameterType.QueryString);
// execute the REST request
var restResponse = await _restClient.Execute<User>(restRequest);
if (restResponse.StatusCode.Equals(HttpStatusCode.OK))
{
user = restResponse.Data;
}
return user;
}
}
My test class :
[TestClass]
public class DataServicesTest
{
public static IRestClient MockRestClient<T>(HttpStatusCode httpStatusCode, string json)
{
var mockIRestClient = new Mock<IRestClient>();
mockIRestClient.Setup(x => x.Execute<T>(It.IsAny<IRestRequest>()))
.Returns(new RestResponse<T>
{
Data = JsonConvert.DeserializeObject<T>(json),
StatusCode = httpStatusCode
});
return mockIRestClient.Object;
}
[TestMethod]
public async void GetUserByUserName()
{
var dataServices = new DataServices(MockRestClient<User>(HttpStatusCode.OK, "my json code"));
var user = await dataServices.GetUserByUserName("User1");
Assert.AreEqual("User1", user.Username);
}
}
But I can't instantiate the RestResponse object, I've the following error:
.Returns(new RestResponse<T>
{
Data = JsonConvert.DeserializeObject<T>(json),
StatusCode = httpStatusCode
});
Cannot access protected internal constructor 'RestResponse' here.
How can I workaround this ? I'm using the FubarCoder.RestSharp nuget package on a Xamarin portable Library.

Mock IRestResponse<T> and return that
public static IRestClient MockRestClient<T>(HttpStatusCode httpStatusCode, string json)
where T : new() {
var data = JsonConvert.DeserializeObject<T>(json)
var response = new Mock<IRestResponse<T>>();
response.Setup(_ => _.StatusCode).Returns(httpStatusCode);
response.Setup(_ => _.Data).Returns(data);
var mockIRestClient = new Mock<IRestClient>();
mockIRestClient
.Setup(x => x.Execute<T>(It.IsAny<IRestRequest>()))
.ReturnsAsync(response.Object);
return mockIRestClient.Object;
}
The test should also be updated to be async as well
[TestMethod]
public async Task GetUserByUserName() {
//Arrange
var client = MockRestClient<User>(HttpStatusCode.OK, "my json code");
var dataServices = new DataServices(client);
//Act
var user = await dataServices.GetUserByUserName("User1");
//Assert
Assert.AreEqual("User1", user.Username);
}

I didn't find any great answers so I ended up writing a helper library. I published it to NuGet - MoqRestSharp.Helpers. This project is aimed to help unit test RestSharp as it extends Mock so this helped me test my RestSharp requests and response error handling.
It uses Moq
NuGet Link
Repository Link - Examples are in the project too
Feedback is always welcome!

Complete solution
using Moq;
using Newtonsoft.Json;
using NUnit.Framework;
using RestSharp;
using System.Net;
namespace RestMockTest
{
public class Tests
{
[Test]
public void Test1()
{
var client = MockRestClient<User>(HttpStatusCode.OK, "{\"Name\":\"User1\"}");
var restRequest = new RestRequest("api/item/", Method.POST);
var restResponse = client.Execute<User>(restRequest);
var user = restResponse.Data;
Assert.AreEqual("User1", user.Name);
}
public static IRestClient MockRestClient<T>(HttpStatusCode httpStatusCode, string json)
where T : new()
{
var data = JsonConvert.DeserializeObject<T>(json);
var response = new Mock<IRestResponse<T>>();
response.Setup(_ => _.StatusCode).Returns(httpStatusCode);
response.Setup(_ => _.Data).Returns(data);
var mockIRestClient = new Mock<IRestClient>();
mockIRestClient
.Setup(x => x.Execute<T>(It.IsAny<IRestRequest>()))
.Returns(response.Object);
return mockIRestClient.Object;
}
}
public class User
{
public string Name { get; set; }
}
}

Related

Http response cannot be deserialized when it's created from test case

I'm trying to run this test:
[TestFixture]
public class AccountManagementViewModelTests
{
[Test, BaseAutoData]
public void OnSync_Should_AddToDatabase_MissingLists(
[Frozen] Mock<IHttpClientService> httpClientService,
[Frozen] Mock<IDataStore<AppModel.List>> dataStore,
List<ApiModel.List> lists,
AccountManagementViewModel sut)
{
// Arrange
httpClientService
.Setup(x => x.GetAsync(It.IsAny<string>()))
.ReturnsAsync(new HttpResponseMessage()
{
Content = JsonContent.Create(lists)
});
// Act
sut.SyncCommand.Execute(null);
// Assert
dataStore.Verify(
d => d.AddItemAsync(
It.Is<AppModel.List>(
x => lists.Any(y => y.Guid == x.ListId))),
Times.Exactly(3));
}
}
Problem is, when I try to deserialize the response, I get 3 empty entities
var allBackedupListsForCurrentUserRequestTask = _httpClientService.GetAsync(string.Format(ListApiEndPoints.GetListsByOwnerEmail, ApplicationUser.Current.Email));
var allBackedupListsForCurrentUserRequest = await allBackedupListsForCurrentUserRequestTask;
var allBackedupListsForCurrentUser = await JsonSerializer.DeserializeAsync<List<ApiModel.List>>(await allBackedupListsForCurrentUserRequest.Content.ReadAsStreamAsync());
If I examine the content of allBackedupListsForCurrentUserRequest.Content I can see all the data there, but none are deserialized into the allBackedupListsForCurrentUser.
The code works fine with real HttpRequests, the issue rises only during unit tests.
What am I missing here?
Other classes involved:
HttpClientService
using ListApp.Services.Interfaces;
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ListApp.Services
{
public class HttpClientService : IHttpClientService
{
private static HttpClient _httpClient = new HttpClient();
public async Task<HttpResponseMessage> GetAsync(string requestUri)
{
return await _httpClient.GetAsync(requestUri);
}
public async Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content)
{
return await _httpClient.PutAsync(requestUri, content);
}
public async Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
return await _httpClient.PostAsync(requestUri, content);
}
public void SetBaseAddress(Uri baseAddress)
{
_httpClient.BaseAddress = baseAddress;
}
}
}
BaseAutoDataAttribute
using AutoFixture;
using AutoFixture.AutoMoq;
using AutoFixture.NUnit3;
using System;
namespace ListApp.UnitTests.DataTtributes
{
[AttributeUsage(AttributeTargets.Method)]
internal class BaseAutoDataAttribute : AutoDataAttribute
{
public BaseAutoDataAttribute() : base(() => CreateFixture()) { }
private static IFixture CreateFixture()
{
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true, GenerateDelegates = true });
return fixture;
}
}
}
Turns out I'm changing the value of JsonSerializerOptions.PropertyNameCaseInsensitive to true when the app starts but not during the tests.
Setting it to true on the [OneTimeSetUp] did the trick.
((JsonSerializerOptions)typeof(JsonSerializerOptions)
.GetField("s_defaultOptions",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic).GetValue(null))
.PropertyNameCaseInsensitive = true;
Solution by andre-ss6.

How to close Resharper test runner after finishing integration test in xUnit Test project (.NET Core)?

i am new to integration tests. I have an xUnit project in my solution which contains one test only.
Here's the definition of my test:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "testoweImie",
LastName = "testoweNazwisko",
MailAddress = "test#test.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
And the BaseIntegrationTest class definition:
public abstract class BaseIntegrationTest
{
private const string TestDatabaseName = "TestDatabase";
protected BaseIntegrationTest()
{
var appFactory = new WebApplicationFactory<Startup>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
RemoveDatabaseContextFromServicesCollectionIfFound<EventStoreContext>(services);
RemoveDatabaseContextFromServicesCollectionIfFound<GrantContext>(services);
services
.AddDbContext<EventStoreContext>(options =>
options.UseInMemoryDatabase(TestDatabaseName))
.AddDbContext<GrantContext>(options =>
options.UseInMemoryDatabase(TestDatabaseName));
});
});
HttpClient = appFactory.CreateClient();
}
protected HttpClient HttpClient { get; }
protected static StringContent SerializeObject(object #object) =>
new StringContent(
JsonConvert.SerializeObject(#object),
Encoding.UTF8,
"application/json");
private static void RemoveDatabaseContextFromServicesCollectionIfFound<T>(IServiceCollection services)
where T : DbContext
{
var descriptor = services.SingleOrDefault(service =>
service.ServiceType == typeof(DbContextOptions<T>));
if (!(descriptor is null))
{
services
.Remove(descriptor);
}
}
}
When i run tests, it takes few seconds, and the test ends successfully. The problem is that Resharper Test Runner still runs, although i've already have collected results. what am i doing wrong here? Do i have to somehow dispose the HttpClient, after performing all tests? If so, how to achieve that? Thanks for any help.
It looks like you're actually booting the application inside the test rather than using the testhost (https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1)
public class BasicTests
: IClassFixture<WebApplicationFactory<RazorPagesProject.Startup>>
{
private readonly WebApplicationFactory<RazorPagesProject.Startup> _factory;
public BasicTests(WebApplicationFactory<RazorPagesProject.Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/About")]
[InlineData("/Privacy")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
Notice the IClassFixture stuff.

Method Not Found when using MOQ in Unit Test

I am having trouble setting up a unit test where I need to Moq the HttpClient. In my code I have a decorator for the HttpClient which follows an interface.
public class WHttpClient: IWHttpClient{
HttpClient _client = new HttpClient();
...
public async Task<HttpReponseMessage> PostAsJsonAsync<T>(string url, T content)
{
//Do Something
return await _client.PostAsJsonAsync(url, content);
}
...
}
public interface IWHttpClient{
HttpRequestHeaders DefaultRequestHeaders {get;}
Task<HttpResponseMessage> PostAsXmlAsync<T>(string url, T content);
Task<HttpResponseMessage> PostAsJsonAsync<T>(string url, T content);
Task<HttpResponseMessage> PostAsync<T>(string url, T content);
Task<HttpResponseMessage> GetAsync(string url);
Task<T> GetAsync<T>(string url);
Task<T> ReadAsAsync<T>(HttpResponseMessage response);
T Read<T>(HttpResponseMessage response);
}
[TestClass]
public class UnitTest1
{
private class WorkClass
{
private IWHttpClient _client;
public WorkClass(IWHttpClient client)
{
_client = client;
}
public void DoWork()
{
var url = "DUMMY";
var content = new ObjectToSerialize();
Task.Run(() => _client.PostAsJsonAsync(url, content));
}
}
public class ObjectToSerialize
{
}
[TestMethod]
public void TestMethod1()
{
Mock<IWHttpClient> _webClientMock = new Mock<IWHttpClient>(MockBehavior.Strict);
var url = "DUMMY";
var content = new ObjectToSerialize();
_webClientMock.Setup(x => x.PostAsJsonAsync(url, It.IsAny<ObjectToSerialize>())).Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
var myClassToTest = new WorkClass(_webClientMock.Object);
myClassToTest.DoWork();
}
}
It successfully builds, but when I run the test, it gives me the exception:
System.MissingMethodException: Method not found: 'System.Threading.Tasks.Task'1 SomeNamespace.IWHttpClient.PostAsJsonAsync(System.String, !!0)'.
I've spent hours trying to figure out why I get this exception when I run the test. I have performed a clean and rebuild of my solution and yet it still appears as well as replacing the inputs in the mocksetup with:
(It.IsAny<string>(), It.IsAny<object>())
Does anyone have an idea what's wrong?
Results from running:
The following minimal example was just to try and reproduce your problem as well as demonstrate how to exercise tests like this.
[TestClass]
public class MyTestClass {
private class WorkClass {
private IWHttpClient _client;
public WorkClass(IWHttpClient client) {
_client = client;
}
public async Task DoWork() {
var url = "DUMMY";
var content = new ObjectToSerialize();
var response = await _client.PostAsJsonAsync(url, content);
}
}
public class ObjectToSerialize {
}
[TestMethod]
public async Task MyTestMethod() {
//Arrange
var expectedResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
var _webClientMock = new Mock<IWHttpClient>(MockBehavior.Strict);
_webClientMock
.Setup(_ => _.PostAsJsonAsync(It.IsAny<string>(), It.IsAny<ObjectToSerialize>()))
.ReturnsAsync(expectedResponse)
.Verifiable();
var myClassToTest = new WorkClass(_webClientMock.Object);
//Act
await myClassToTest.DoWork();
//Assert
_webClientMock.Verify();
}
}
When exercised the test behaved as expected and passed. Even when the setup was changed to
.Setup(_ => _.PostAsJsonAsync(It.IsAny<string>(), It.IsAny<object>()))
Review and compare to your current test to help identify where possible mistakes may have been made.

Testing Polly retry policy with moq

I'm trying to write a unit test for polly, but it looks like the return is cached.
Method PostAsyncWithRetry:
using Polly;
using System;
using System.Diagnostics;
using System.Net.Cache;
using System.Net.Http;
public class RetryClient
{
private HttpClient httpClient = new HttpClient(new WebRequestHandler()
{ CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore) });
public HttpResponseMessage PostAsyncWithRetry(
String url,
String path,
StringContent httpContent)
{
httpClient.BaseAddress = new Uri(url);
var retryPolicy =
Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.RetryAsync(3, (exception, retryCount, context) =>
{
Debug.WriteLine("RetryCount: {0}", retryCount);
});
var response = retryPolicy.ExecuteAsync(async () =>
{
return await httpClient.PostAsync(path, httpContent);
}
);
return response.Result;
}
}
Test:
[TestFixture]
class Test
{
private HttpClient mockHTTPClient;
private Mock<WebRequestHandler> mockHttpMessageHandler;
private RetryClient testInstance;
private const String URL = "https://aaa.com";
private const String PATH = "/path";
private const String EXPECTED_STRING_CONTENT = "Some return text";
[SetUp]
public void SetUp()
{
testInstance = new RetryClient();
mockHttpMessageHandler = new Mock<WebRequestHandler>();
mockHttpMessageHandler.Object.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
mockHTTPClient = new HttpClient(mockHttpMessageHandler.Object);
var type = typeof(RetryClient);
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
fields[0].SetValue(testInstance, mockHTTPClient);
}
[Test]
public void TestMEEEE()
{
var responses = new Queue<Task<HttpResponseMessage>>();
responses.Enqueue(Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.NotFound,
Content = new StringContent(EXPECTED_STRING_CONTENT)
}));
responses.Enqueue(Task.FromResult(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(EXPECTED_STRING_CONTENT)
}));
var postContent = new StringContent(EXPECTED_STRING_CONTENT);
mockHttpMessageHandler.Protected()
.Setup<Task>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns(responses.Dequeue());
var response = testInstance.PostAsyncWithRetry(
URL, PATH, postContent);
mockHttpMessageHandler.Verify();
Assert.AreEqual(responses.Count, 0, "didn't dequeue");
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, "Incorrect status code");
}
}
Not sure why, but it looks like the responses queue is only being Dequeue once, this leads me to believe the response is being cache. Does anyone know who is caching the response, and how do I disable it?
Thanks a bunch in advance!
I figured it out. It has nothing to do with caching. During the mock setup, it stores the Dequeue value as a return instead of invoking it every time.
Changing it to () => responses.Dequeue() works now.
Thank you guys!

How do I use NinjectSelfHostBootstrapper in my tests and have more than one test pass?

Here's my NUnit test code (simplified to isolate the issue):
public class MyController : ApiController
{
[HttpGet]
[Route("api/route")]
public string Route1()
{
return "route";
}
}
[TestFixture]
public class MyTestFixture
{
private NinjectSelfHostBootstrapper _bootstrapper;
[SetUp]
public void SetUp()
{
var config = new HttpSelfHostConfiguration("http://localhost:8767");
config.MapHttpAttributeRoutes();
var kernel = new StandardKernel();
this._bootstrapper = new NinjectSelfHostBootstrapper(() => kernel, config);
this._bootstrapper.Start();
}
[TearDown]
public void TearDown()
{
if (this._bootstrapper != null)
{
this._bootstrapper.Dispose();
}
}
[Test]
public void MyTest1()
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8767/api/route");
var response = client.SendAsync(request).Result;
var content = response.Content.ReadAsStringAsync().Result;
Assert.That(content, Is.EqualTo("\"route\""));
}
}
[Test]
public void MyTest2()
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8767/api/route");
var response = client.SendAsync(request).Result;
var content = response.Content.ReadAsStringAsync().Result;
Assert.That(content, Is.EqualTo("\"route\""));
}
}
}
When I run that using the default NinjectSelfHostBootstrapper the first test passes fine, but the second throws an exception, saying:
A registration already exists for URI 'http://localhost:8767/'.
When I examine the code for these two files:
https://github.com/ninject/Ninject.Web.WebApi/blob/master/src/Ninject.Web.WebApi.Selfhost/NinjectWebApiSelfHost.cs
https://github.com/ninject/Ninject.Web.Common/blob/master/src/Ninject.Web.Common.SelfHost/NinjectSelfHostBootstrapper.cs
I can see why - an HttpSelfHostServer is created in the latter and OpenAsync() is called on the server, but CloseAsync() isn't called on the server - is this a bug or am I doing something wrong? How do I get NinjectSelfHostBootstrapper to work with subsequently called tests?

Categories