Skip to content

Instantly share code, notes, and snippets.

@yohanmishkin
Last active January 11, 2018 04:43
Show Gist options
  • Save yohanmishkin/3e4eff4a7cda8f16a6d025dcd38600f9 to your computer and use it in GitHub Desktop.
Save yohanmishkin/3e4eff4a7cda8f16a6d025dcd38600f9 to your computer and use it in GitHub Desktop.
Testing the outer boundary of a JSONAPI REST API in .Net (using SimpleInjector & Saule)
using Controllers;
using Core.Entities;
using Tests.Integration.Common;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Xunit;
namespace Tests.Integration.Todos.Web
{
public class TodoTests : WebTest
{
public TodoTests()
{
Container.Register<TodoController>();
}
[Theory]
[MemberData(nameof(TestTodos))]
public void GetTodos(List<Todo> testData)
{
Container.Register<ITodoUseCase>(() => new TodoUseCaseStub(testData));
var result = Client.GetAsync("api/todos").Result;
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
Assert.Equal("todo", Type(json));
Assert.Equal(testData.Count, Count(json));
AssertTodoAttributes(testData.First(), json);
}
[Theory]
[MemberData(nameof(TestTodo))]
public void GetTodo(Todo todo)
{
Container.Register<ITodoUseCase>(() => new TodoUseCaseStub(new List<Todo> { todo }));
var result = Client.GetAsync($"api/todos/{todo.Id}").Result;
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
AssertTodoAttributes(todo, json);
AssertTodoRelationships(todo, json);
}
[Theory]
[MemberData(nameof(TestTodo))]
public void PostTodo(Todo todo)
{
var id = 1234;
var expectedCreatedAt = new DateTime(2012, 12, 12);
todo.CreatedAt = expectedCreatedAt;
Container.Register<ITodoUseCase>(() => new TodoUseCaseStub(id, expectedCreatedAt));
var payload =
$@"{{
""data"":{{
""attributes"":{{
""todo-type"":""{todo.TodoType}"",
""todo-group"":null,
""message"":""{todo.Message}"",
""explanation"":null,
""status"":""{todo.Status}"",
""created-at"":null,
""completed-at"":null
}},
""relationships"":{{
""assigned-to"":{{
""data"":{{
""type"":""users"",
""id"":""{todo.AssigneeUser.Id}""
}}
}},
""assigned-from"":{{
""data"":{{
""type"":""users"",
""id"":""{todo.AssignorUser.Id}""
}}
}}
}},
""type"":""todos""
}}
}}";
var result = Client.PostAsync("api/todos", payload).Result;
Assert.Equal(HttpStatusCode.Created, result.StatusCode);
var json = JObject.Parse(result.Content.ReadAsStringAsync().Result);
Assert.Equal("todo", Type(json));
Assert.Equal(id, Id<int>(json));
AssertTodoAttributes(todo, json);
AssertTodoRelationships(todo, json);
}
private void AssertTodoAttributes(Todo expected, JObject json)
{
Assert.Equal(expected.Message, Attribute<string>(json, "message"));
Assert.Equal(expected.Explanation, Attribute<string>(json, "explanation"));
Assert.Equal(expected.TodoType, Attribute<string>(json, "todo-type"));
Assert.Equal(expected.TodoType.TodoGroup, Attribute<string>(json, "todo-group"));
Assert.Equal(expected.Status, Attribute<string>(json, "status"));
Assert.Equal(expected.IsCompleted, Attribute<bool>(json, "is-completed"));
Assert.Equal(expected.CompletedAt, Attribute<DateTime?>(json, "completed-at"));
Assert.Equal(expected.CreatedAt, Attribute<DateTime?>(json, "created-at"));
}
private void AssertTodoRelationships(Todo expected, JObject json)
{
Assert.Equal(expected.AssigneeUser.Id, Relationship<string>(json, "assigned-to"));
Assert.Equal(expected.AssignorUser.Id, Relationship<string>(json, "assigned-from"));
}
public static TheoryData<Todo> TestTodo =>
new TheoryData<Todo>
{
new Todo()
{
Id = 1234,
Message = "Hello",
Status = "Pending",
AssigneeUser = new User("1024"),
AssignorUser = new User("999")
}
};
public static TheoryData<List<Todo>> TestTodos =>
new TheoryData<List<Todo>>
{
new List<Todo>
{
new Todo() { CompletedAt = new DateTime(2012, 12, 12) }
},
new List<Todo>
{
new Todo(),
new Todo(),
new Todo(),
new Todo(),
new Todo()
}
};
}
}
using Microsoft.Owin.Testing;
using Newtonsoft.Json.Linq;
using Owin;
using Saule.Http;
using SimpleInjector;
using SimpleInjector.Integration.WebApi;
using SimpleInjector.Lifestyles;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
namespace Tests.Integration.Common
{
public class WebTest
{
private static TestServer WebServer { get; } = TestServer.Create<TestStartup>();
protected static Container Container { get; private set; }
public WebTest()
{
Container = new Container();
Container.Options.AllowOverridingRegistrations = true;
Container.Options.DefaultLifestyle = Lifestyle.Scoped;
Container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
TestStartup.HttpConfiguration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(Container);
}
protected HttpClient Client
{
get
{
Container.Verify();
var client = WebServer.HttpClient;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/vnd.api+json")
);
return client;
}
}
protected static int Count(JObject json)
{
return json["data"].Count();
}
protected static string Type(JObject json)
{
if (json["data"].Type == JTokenType.Object)
{
return json["data"].Value<string>("type");
}
return json["data"].First().Value<string>("type");
}
protected TResult Id<TResult>(JObject json)
{
return json["data"].Value<TResult>("id");
}
protected TResult Attribute<TResult>(JObject json, string attributeName)
{
if (json["data"].Type == JTokenType.Object)
{
return json["data"]["attributes"].Value<TResult>(attributeName);
}
return json["data"].First()["attributes"].Value<TResult>(attributeName);
}
protected TResult Relationship<TResult>(JObject json, string relationshipName)
{
if (json["data"].Type == JTokenType.Object)
{
return json["data"]["relationships"][relationshipName]["data"].Value<TResult>("id");
}
return json["data"].First()["relationships"][relationshipName]["data"].Value<TResult>("id");
}
}
public static class HttpClientExtensions
{
public static async Task<HttpResponseMessage> PostAsync(this HttpClient client, string url, string payload)
{
HttpContent content =
new StringContent(
payload,
Encoding.UTF8,
"application/vnd.api+json"
);
content.Headers.ContentType.Parameters.Clear();
return await client.PostAsync(url, content);
}
}
internal class TestStartup
{
public static HttpConfiguration HttpConfiguration { get; private set; }
public void Configuration(IAppBuilder app)
{
HttpConfiguration = new HttpConfiguration();
HttpConfiguration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
HttpConfiguration.ConfigureJsonApi();
HttpConfiguration.MapHttpAttributeRoutes();
app.UseWebApi(HttpConfiguration);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment