Last active
January 11, 2018 04:43
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
}; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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