I've written in the past about how to use the PATCH verb for updating individual properties on larger objects in a RESTful way. When it comes time to test those controller methods, it helps to have a way to reliably setup your test data. That's what this post is about.
I created a static class that I can use from my tests to create a JsonPatchDocument to pass to your tests. Here's what that class looks like:
1: using Microsoft.AspNetCore.JsonPatch;
2: using System;
3: using System.Linq.Expressions;
4:
5: namespace Blog.UnitTests.Utilities.Setup
6: {
7: public static class JsonPatchDocumentSetup
8: {
9: public static JsonPatchDocument<T> BuildJsonPatchDocument<T, TProp>(Expression<Func<T, TProp>> expr, TProp newObject, string type) where T : class
10: {
11: var jsonPatchDocument = new JsonPatchDocument<T>();
12:
13: switch (type)
14: {
15: case "add":
16: jsonPatchDocument.Add(expr, newObject);
17: break;
18: case "copy":
19: jsonPatchDocument.Copy(expr, expr);
20: break;
21: case "move":
22: jsonPatchDocument.Move(expr, expr);
23: break;
24: case "remove":
25: jsonPatchDocument.Remove(expr);
26: break;
27: case "replace":
28: jsonPatchDocument.Replace(expr, newObject);
29: break;
30: }
31:
32: return jsonPatchDocument;
33: }
34:
35: public static JsonPatchDocument<T> AppendToJsonPatchDocument<T, TProp>(this JsonPatchDocument<T> jsonPatchDocument, Expression<Func<T, TProp>> expr, TProp newObject, string type) where T : class
36: {
37: switch (type)
38: {
39: case "add":
40: jsonPatchDocument.Add(expr, newObject);
41: break;
42: case "copy":
43: jsonPatchDocument.Copy(expr, expr);
44: break;
45: case "move":
46: jsonPatchDocument.Move(expr, expr);
47: break;
48: case "remove":
49: jsonPatchDocument.Remove(expr);
50: break;
51: case "replace":
52: jsonPatchDocument.Replace(expr, newObject);
53: break;
54: }
55:
56: return jsonPatchDocument;
57: }
58: }
59: }
And here are some examples of how you'd use it:
1: using Blog.BusinessLogic.Implementations;
2: using System.Collections.Generic;
3: using System.Threading.Tasks;
4: using Xunit;
5:
6: namespace Blog.BusinessLogic.UnitTests.Implementations
7: {
8: public class BlogClassUnitTests
9: {
10: ...test class setup...
11: [Theory]
12: [InlineData(true, true)]
13: [InlineData(true, false)]
14: [InlineData(true, null)]
15: [InlineData(null, true)]
16: [InlineData(null, false)]
17: [InlineData(null, null)]
18: [InlineData(false, true)]
19: [InlineData(false, false)]
20: [InlineData(false, null)]
21: public async Task DoingTheThingShouldDoWhatIsExpectedUsingInlineData(bool? firstUpdatedValue, bool? secondUpdatedValue)
22: {
23: // arrange
24: var jsonPatchDocument = JsonPatchDocumentSetup.BuildJsonPatchDocument<UpdateableViewModel, bool?>(p => p.FirstUpdateableProperty, firstUpdatedValue, "replace");
25: jsonPatchDocument.AppendToJsonPatchDocument(p => p.SecondUpdateableProperty, secondUpdatedValue, "replace");
26: ...additional setup...
27:
28: // act
29: ...take action...
30:
31: // assert
32: ...make assertions...
33: }
34:
35: public static IEnumerable<object[]> BlogTestCases
36: {
37: get
38: {
39: return new[]
40: {
41: new object[]
42: {
43: new UpdateableViewModel
44: {
45: FirstUpdateableProperty = true
46: NestedUpdateableProperty = new NestedUpdateableViewModel()
47: SecondUpdateableProperty = false
48: },
49: (Expression<Func<UpdateableViewModel, NestedUpdateableViewModel>>)(p => p.NestedUpdateableProperty),
50: new NestedUpdateableViewModel{Id = 987},
51: new UpdateableViewModel
52: {
53: FirstUpdateableProperty = true
54: NestedUpdateableProperty = new NestedUpdateableViewModel{Id = 987}
55: SecondUpdateableProperty = false
56: }
57: }
58: }
59: }
60: }
61:
62: [Theory, MemberData(nameof(BlogTestCases))]
63: public async Task DoingTheThingShouldDoWhatIsExpectedUsingMemberData<TProp1, TProp2>(UpdateableViewModel currentViewModel, Expression<Func<UpdateableViewModel, TProp1>> expression, TProp1 newObject, UpdateableViewModel expected)
64: {
65: // arrange
66: var jsonPatchDocument = JsonPatchDocumentSetup.BuildJsonPatchDocument<(expression, newObject, "replace");
67: ...additional setup...
68:
69: // act
70: ...take action...
71:
72: // assert
73: ...make assertions...
74: }
75: }
76: }
Hopefully next time I need this I remember I wrote this post, or maybe somebody else comes across this and it helps them with unit testing their own code. Maybe some day I'll turn this into a NuGet package. Ah, to dream.
No comments:
Post a Comment