I have this Service:
public class PlayerService : Service
{
public IPlayerAppService PlayerAppService { get; set; }
public PlayerService (IPlayerAppService service)
{
if (service == null)
throw new ArgumentException ("Service null");
PlayerAppService = service;
}
public object Post (PlayerDTO request)
{
var newPlayer = new PlayerResponse ()
{
Player = PlayerAppService.SendPlayerLocation(request.Position.Latitude, request.Position.Longitude)
};
return new HttpResult (newPlayer)
{
StatusCode = System.Net.HttpStatusCode.Created,
Headers =
{
{ HttpHeaders.Location, base.Request.AbsoluteUri.CombineWith(newPlayer.Player.Id.ToString()) }
}
};
}
}
I've manually verified that the Location and the Response looks correct from my deployments of this service. I would like to figure out how to unit test this though. I wrote a test like this:
[TestFixture]
public class PlayerServiceTests
{
AppHost appHost;
[TestFixtureSetUp]
public void TestFixtureSetUp ()
{
appHost = new AppHost ();
appHost.Init ();
appHost.Start ("http://localhost:1337/");
}
[TestFixtureTearDown]
public void TestFixtureTearDown ()
{
appHost.Dispose ();
appHost = null;
}
[Test]
public void NewPlayer_Should_Return201AndLocation ()
{
// Arrange
PlayerService service = new PlayerService (appHost.TryResolve<IPlayerAppService>());
// Act
HttpResult response = (HttpResult)service.Post (It.IsAny<PlayerDTO>());
// Assert
Assert.NotNull (response);
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
Assert.AreEqual(response.Response.ToDto<PlayerResponse>().Player.Id.ToString(), response.Headers.Where(x=> x.Key == HttpHeaders.Location).SingleOrDefault().Value);
}
}
The base.Request when my unit test runs though. Do you have any suggestions on how I can populate this from my unit test?
You're using an self-hosting HttpListener as you would for an integration test, but you're not doing in integration test.
An integration test would look like:
[Test]
public void NewPlayer_Should_Return201AndLocation ()
{
var client = new JsonServiceClient("http://localhost:1337/") {
ResponseFilter = httpRes => {
//Test response headers...
};
}
PlayerResponse response = client.Post(new Player { ... });
}
Otherwise if you want to do an unit test you don't need an AppHost and can just test the PlayerService class just like any other C# class, injecting all the dependencies and the mock Request context it needs.
[Test]
public void NewPlayer_Should_Return201AndLocation ()
{
var mockCtx = new Mock<IRequestContext>();
mockCtx.SetupGet (f => f.AbsoluteUri).Returns("localhost:1337/player");
PlayerService service = new PlayerService {
MyOtherDependencies = new Mock<IMyOtherDeps>().Object,
RequestContext = mockCtx.Object,
};
HttpResult response = (HttpResult)service.Post(new Player { ... });
//Assert stuff..
}
Related
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.
I am trying to create a test for a situation where an Update request throws an exception. Is this possible to do using FakeXRMEasy? I have tried using AddFakeMessageExecutor, but at the moment it is not working:
My fake message executor class:
public class UpdateExecutor : IFakeMessageExecutor
{
public bool CanExecute(OrganizationRequest request)
{
return request is UpdateRequest;
}
public OrganizationResponse Execute(
OrganizationRequest request,
XrmFakedContext ctx)
{
throw new Exception();
}
public Type GetResponsibleRequestType()
{
return typeof(UpdateRequest);
}
}
Use in test:
fakeContext.Initialize(new Entity[] { agreement });
fakeContext.AddFakeMessageExecutor<UpdateRequest>(new UpdateExecutor());
fakeContext.ExecuteCodeActivity<AgreementConfirmationWorkflow>(fakeContext.GetDefaultWorkflowContext());
And in the workflow the update request is called:
var workflowContext = executionContext.GetExtension<IWorkflowContext>();
var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
/// some code to retrieve entity and change attributes ///
service.Update(entity);
I wanted this to throw an exception, but at the moment the update request is completing successfully. How can I make this work?
IFakeMessageExecutor only works when you call IOrganizationService.Execute method. So, if you change your service.Update(entity); line of code for service.Execute(new UpdateRequest { Target = entity}); it should work.
Here is a full working example for reference:
CodeActivity
public class RandomCodeActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
var workflowContext = context.GetExtension<IWorkflowContext>();
var serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
var service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
var accountToUpdate = new Account() { Id = new Guid("e7efd527-fd12-48d2-9eae-875a61316639"), Name = "A new faked name!" };
service.Execute(new UpdateRequest { Target = accountToUpdate });
}
}
FakeMessageExecutor instance
public class FakeUpdateRequestExecutor : IFakeMessageExecutor
{
public bool CanExecute(OrganizationRequest request)
{
return request is UpdateRequest;
}
public OrganizationResponse Execute(OrganizationRequest request, XrmFakedContext ctx)
{
throw new InvalidPluginExecutionException("Throwing an Invalid Plugin Execution Exception for test purposes");
}
public Type GetResponsibleRequestType()
{
return typeof(UpdateRequest);
}
}
Test (uses xUnit test lib)
[Fact]
public void UpdateAccount_WithUpdateExecutorThrowingAnException_ExceptionThrown()
{
//Assign
var context = new XrmFakedContext
{
ProxyTypesAssembly = Assembly.GetAssembly(typeof(Account))
};
var account = new Account() { Id = new Guid("e7efd527-fd12-48d2-9eae-875a61316639"), Name = "Faked Name" };
context.Initialize(new List<Entity>() { account });
context.AddFakeMessageExecutor<UpdateRequest>(new FakeUpdateRequestExecutor());
var service = context.GetOrganizationService();
//Act
//Assert
Assert.Throws<InvalidPluginExecutionException>(() => context.ExecuteCodeActivity<RandomCodeActivity>(account));
}
I have an ApiController and would like to test it with unit tests including the routing.
An example:
[RoutePrefix("prefix")]
public class Controller : ApiController
{
[HttpGet]
[Route("{id1}")]
public int Add(int id1, [FromUri] int id2)
{
return id1 + id2;
}
}
I would now like to test this method. I see, that I can test it like an ordinary method. But I would also like to test it with the translation of the URL to the method parameters.
Basically I would like to have an automatic test where I call a URL like prefix/10?id2=5 and get a result of 15. Is this somehow possible?
I wrote a little helper class for in-memory integration testing that can be called as part of the test suit.
internal interface IHttpTestServer : IDisposable {
HttpConfiguration Configuration { get; }
HttpClient CreateClient();
}
internal class HttpTestServer : IHttpTestServer {
HttpServer httpServer;
public HttpTestServer(HttpConfiguration configuration = null) {
httpServer = new HttpServer(configuration ?? new HttpConfiguration());
}
public HttpConfiguration Configuration {
get { return httpServer.Configuration; }
}
public HttpClient CreateClient() {
var client = new HttpClient(httpServer);
return client;
}
public void Dispose() {
if (httpServer != null) {
httpServer.Dispose();
httpServer = null;
}
}
public static IHttpTestServer Create(HttpConfiguration configuration = null) {
return new HttpTestServer(configuration);
}
}
And would then use it like this
[TestMethod]
public async Task HttpClient_Should_Get_OKStatus_From_InMemory_Hosting() {
using (var server = new HttpTestServer()) {
MyWebAPiProjectNamespace.WebApiConfig.Configure(server.Configuration);
var client = server.CreateClient();
string url = "http://localhost/prefix/10?id2=5";
var expected = 15;
var request = new HttpRequestMessage {
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var response = await client.SendAsync(request)) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
var result = await response.Content.ReadAsAsync<int>();
Assert.AreEqual(expected, result);
}
}
}
This will configure an in-memory test server that the test can make calls to using its httpclient. It is in essence an end-to-end integration test.
Create an OWIN StartUp class using Microsoft ASP.NET Web API 2.2 OWIN package:
public class Startup
{
public void Configuration(IAppBuilder builder)
{
var config = new HttpConfiguration();
builder.UseWebApi(config);
config.MapHttpAttributeRoutes();
config.EnsureInitialized();
}
}
Use Microsoft ASP.NET Web API 2.2 Self Host package in your tests (used NUnit for example):
[Test]
[TestCase(10, 5, 15)]
[TestCase(1, 2, 3)]
// add your test cases
public async Task AdditionTests(int a, int b, int result)
{
// Arrange
var address = "http://localhost:5050";
using (WebApp.Start<Startup>(address))
{
var client = new HttpClient();
var requestUri = $"{address}/prefix/{a}?id2={b}";
// Act
var response = await client.GetAsync(requestUri);
// Assert
Assert.IsTrue(await response.Content.ReadAsAsync<int>() == result);
}
}
That is an integration test, not a unit test. If you wanted to automate this you would have to have a tool that would launch/host your web api and then execute requests against it.
If you wanted to keep it as a unit test though you could validate the attributes on the class and the method and check the values.
var type = typeof(Controller);
var attributeRoutePrefix = type.GetCustomAttribute(typeof(RoutePrefixAttribute)) as RoutePrefixAttribute;
Assert.IsNotNull(attributeRoutePrefix);
Assert.AreEqual("prefix", attributeRoutePrefix.Prefix);
var methodAttribute = type.GetMethod(nameof(Controller.Add)).GetCustomAttribute(typeof(RouteAttribute)) as RouteAttribute;
Assert.IsNotNull(methodAttribute);
Assert.AreEqual("id1", methodAttribute.Template);
Its possible by using postman or fiddler to test with url param..
When I curl to the /test route it works fine, however the test below 404's when trying to hit the in memory server on the same route.
When inspecting _client and _config appear to be ok - although I am not sure how to confirm that my in memory server is functioning correctly.
Does anybody know how I can get my in memory web server to map it's routes correctly so my test method can reach it?
namespace Robo.Tests.Controllers
{
[TestClass]
public class IntegrationTests
{
private HttpMessageInvoker _client;
private HttpConfiguration _config = new HttpConfiguration();
[TestInitialize]
public void SetupTest()
{
_config.MapHttpAttributeRoutes();
_config.EnsureInitialized();
var server = new HttpServer(_config);
_client = new HttpMessageInvoker(server);
}
[TestMethod]
public async Task Test()
{
var result = await _client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://localhost/test"), CancellationToken.None);
}
}
}
and controller in case you are interested
namespace Robo.Controllers
{
//[ValidationActionFilter]
public class CVController : ApiController
{
[HttpGet]
[Route("test")]
public async Task<IHttpActionResult> test()
{
return Ok();
}
}
}
For in-memory server testing The following utility class was created. It basically wraps the setup functionality in the example shown.
internal interface IHttpTestServer : IDisposable {
HttpConfiguration Configuration { get; }
HttpClient CreateClient();
}
internal class HttpTestServer : IHttpTestServer {
HttpServer httpServer;
public HttpTestServer(HttpConfiguration configuration = null) {
httpServer = new HttpServer(configuration ?? new HttpConfiguration());
}
public HttpConfiguration Configuration {
get { return httpServer.Configuration; }
}
public HttpClient CreateClient() {
var client = new HttpClient(httpServer);
return client;
}
public void Dispose() {
if (httpServer != null) {
httpServer.Dispose();
httpServer = null;
}
}
public static IHttpTestServer Create(HttpConfiguration configuration = null) {
return new HttpTestServer(configuration);
}
}
The following test was crafted to demonstrate the use of in memory server using OP
[TestClass]
public class IntegrationTests {
[TestMethod]
public async Task Test() {
using (var server = HttpTestServer.Create()) {
//Arrange
var config = server.Configuration;
config.MapHttpAttributeRoutes();
config.EnsureInitialized();
var client = server.CreateClient();
var url = "http://localhost/test";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var expected = System.Net.HttpStatusCode.OK;
//Act
var result = await client.SendAsync(request, CancellationToken.None);
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(expected, result.StatusCode);
}
}
public class CVController : ApiController {
[HttpGet]
[Route("test")]
public async Task<IHttpActionResult> test() {
return Ok();
}
}
}
Test passes.
The thing about this example is that the test and controller exist in same assembly so map attribute scans the assembly it was called in and found API controller with attribute routes. If controller lived in another project then the web API config of that project should be called on the HttpConfiguration to properly configure web API.
UPDATE
The test project and web api project should be two separate projects. That said, The web project should have a WebApiConfig.cs file with a static WebApiConfig.Register class and method. That method takes a HttpConfiguration parameter. The test should use that method to configure the api for in memory calls.
[TestClass]
public class IntegrationTests {
[TestMethod]
public async Task Test() {
using (var server = HttpTestServer.Create()) {
//Arrange
var config = server.Configuration;
//Config server
MyWebApiNamespace.WebApiConfig.Register(config);
var client = server.CreateClient();
var url = "http://localhost/test";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var expected = System.Net.HttpStatusCode.OK;
//Act
var result = await client.SendAsync(request, CancellationToken.None);
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(expected, result.StatusCode);
}
}
}
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?