From 8eb4cef41c93f33e896eb7986b4f0948daa7d6b5 Mon Sep 17 00:00:00 2001
From: Lawrence Leong <45556912+Rexrover2@users.noreply.github.com>
Date: Mon, 27 May 2024 14:26:19 +1000
Subject: [PATCH] Added example to display error of AppendHttpCallStub when
there are query params with curly brackets
* Added complex parameter logic as example in controller, service and proxy.
* Re-added previously overwritten test
* Separated tests to different files
---
.../MovieProject.Logic/DTO/UserSearchModel.cs | 27 +++++++
.../Extensions/UserMapperExtensions.cs | 20 ++++++
.../MovieProject.Logic.csproj | 4 ++
.../Proxy/DTO/UserSearchModel.cs | 24 +++++++
.../MovieProject.Logic/Proxy/UserProxy.cs | 20 ++++++
.../MovieProject.Logic/Service/UserService.cs | 15 +++-
.../ComponentTesting/SearchUserHappyTests.cs | 72 +++++++++++++++++++
.../Controllers/UserController.cs | 11 +++
8 files changed, 190 insertions(+), 3 deletions(-)
create mode 100644 Examples/MovieProject/MovieProject.Logic/DTO/UserSearchModel.cs
create mode 100644 Examples/MovieProject/MovieProject.Logic/Extensions/UserMapperExtensions.cs
create mode 100644 Examples/MovieProject/MovieProject.Logic/Proxy/DTO/UserSearchModel.cs
create mode 100644 Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/SearchUserHappyTests.cs
diff --git a/Examples/MovieProject/MovieProject.Logic/DTO/UserSearchModel.cs b/Examples/MovieProject/MovieProject.Logic/DTO/UserSearchModel.cs
new file mode 100644
index 0000000..cdfe2b5
--- /dev/null
+++ b/Examples/MovieProject/MovieProject.Logic/DTO/UserSearchModel.cs
@@ -0,0 +1,27 @@
+namespace MovieProject.Logic.DTO
+{
+ using System.Text.Json.Serialization;
+
+ namespace MovieProject.Logic.Proxy.DTO
+ {
+
+ public class UserSearchModel
+ {
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("username")]
+ public string Username { get; set; }
+
+ [JsonPropertyName("email")]
+ public string Email { get; set; }
+
+ [JsonPropertyName("phone")]
+ public string Phone { get; set; }
+
+ [JsonPropertyName("website")]
+ public string Website { get; set; }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Examples/MovieProject/MovieProject.Logic/Extensions/UserMapperExtensions.cs b/Examples/MovieProject/MovieProject.Logic/Extensions/UserMapperExtensions.cs
new file mode 100644
index 0000000..9f49cce
--- /dev/null
+++ b/Examples/MovieProject/MovieProject.Logic/Extensions/UserMapperExtensions.cs
@@ -0,0 +1,20 @@
+using MovieProject.Logic.Proxy.DTO;
+
+namespace MovieProject.Logic.Extensions
+{
+ public static class UserMapperExtensions
+ {
+ public static UserSearchModel MapUserSearchModelDtoToProxyDto(
+ this DTO.MovieProject.Logic.Proxy.DTO.UserSearchModel searchModel)
+ {
+ return new UserSearchModel
+ {
+ Name = searchModel.Name,
+ Username = searchModel.Username,
+ Email = searchModel.Email,
+ Phone = searchModel.Phone,
+ Website = searchModel.Website
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Examples/MovieProject/MovieProject.Logic/MovieProject.Logic.csproj b/Examples/MovieProject/MovieProject.Logic/MovieProject.Logic.csproj
index e32b73d..e155f33 100644
--- a/Examples/MovieProject/MovieProject.Logic/MovieProject.Logic.csproj
+++ b/Examples/MovieProject/MovieProject.Logic/MovieProject.Logic.csproj
@@ -22,4 +22,8 @@
+
+
+
+
diff --git a/Examples/MovieProject/MovieProject.Logic/Proxy/DTO/UserSearchModel.cs b/Examples/MovieProject/MovieProject.Logic/Proxy/DTO/UserSearchModel.cs
new file mode 100644
index 0000000..da00c03
--- /dev/null
+++ b/Examples/MovieProject/MovieProject.Logic/Proxy/DTO/UserSearchModel.cs
@@ -0,0 +1,24 @@
+using System.Text.Json.Serialization;
+
+namespace MovieProject.Logic.Proxy.DTO
+{
+
+ public class UserSearchModel
+ {
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("username")]
+ public string Username { get; set; }
+
+ [JsonPropertyName("email")]
+ public string Email { get; set; }
+
+ [JsonPropertyName("phone")]
+ public string Phone { get; set; }
+
+ [JsonPropertyName("website")]
+ public string Website { get; set; }
+ }
+
+}
diff --git a/Examples/MovieProject/MovieProject.Logic/Proxy/UserProxy.cs b/Examples/MovieProject/MovieProject.Logic/Proxy/UserProxy.cs
index 48b0ba6..201b8a7 100644
--- a/Examples/MovieProject/MovieProject.Logic/Proxy/UserProxy.cs
+++ b/Examples/MovieProject/MovieProject.Logic/Proxy/UserProxy.cs
@@ -2,13 +2,17 @@
using System;
using System.Net;
using System.Net.Http;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using MovieProject.Logic.Proxy.DTO;
namespace MovieProject.Logic.Proxy
{
public interface IUserProxy
{
Task GetUsers();
+ Task GetSearchUsers(UserSearchModel searchModel);
}
public class UserProxy : BaseProxy, IUserProxy
@@ -36,5 +40,21 @@ public UserProxy(HttpClient client,
return result;
}
+
+ public async Task GetSearchUsers(UserSearchModel searchModel)
+ {
+ string serialisedQueryParameter = JsonSerializer.Serialize(searchModel);
+ string route = $"searchUsers?userSearchModel={serialisedQueryParameter}";
+
+ var result = await Send(HttpMethod.Get, route, (DTO.User[] users, HttpStatusCode status) =>
+ {
+ if(status != HttpStatusCode.OK || users == null)
+ throw new Exception("Unexpected response");
+
+ return users;
+ });
+
+ return result;
+ }
}
}
diff --git a/Examples/MovieProject/MovieProject.Logic/Service/UserService.cs b/Examples/MovieProject/MovieProject.Logic/Service/UserService.cs
index 7f5f723..fbf0af2 100644
--- a/Examples/MovieProject/MovieProject.Logic/Service/UserService.cs
+++ b/Examples/MovieProject/MovieProject.Logic/Service/UserService.cs
@@ -1,13 +1,15 @@
-using MovieProject.Logic.Proxy;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MovieProject.Logic.Proxy;
+using MovieProject.Logic.Proxy.DTO;
-namespace MovieProject.Logic
+namespace MovieProject.Logic.Service
{
public interface IUserService
{
Task> GetUsers();
+ Task> GetSearchUsers(UserSearchModel searchModel);
}
public class UserService : IUserService
@@ -25,5 +27,12 @@ public async Task> GetUsers()
return users.Select(c=> c.Name).OrderBy(c=> c).ToList();
}
+
+ public async Task> GetSearchUsers(UserSearchModel searchModel)
+ {
+ var users = await _userProxy.GetSearchUsers(searchModel);
+
+ return users.Select(c=> c.Name).OrderBy(c=> c).ToList();
+ }
}
}
diff --git a/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/SearchUserHappyTests.cs b/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/SearchUserHappyTests.cs
new file mode 100644
index 0000000..57b6ced
--- /dev/null
+++ b/Examples/MovieProject/MovieProject.Tests/MovieProject.IsolatedTests/ComponentTesting/SearchUserHappyTests.cs
@@ -0,0 +1,72 @@
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using FluentAssertions;
+using FluentAssertions.Execution;
+using IsolatedTests.ComponentTestings;
+using MovieProject.Logic.DTO.MovieProject.Logic.Proxy.DTO;
+using Newtonsoft.Json;
+using SystemTestingTools;
+using Xunit;
+
+namespace MovieProject.IsolatedTests.ComponentTesting
+{
+ [Collection("SharedServer collection")]
+ [Trait("Project", "User Component Tests (Happy)")]
+ public class SearchUserHappyTests
+ {
+ private readonly TestServerFixture Fixture;
+
+ private static string Url = "https://jsonplaceholder.typicode.com/searchUsers";
+
+ public SearchUserHappyTests(TestServerFixture fixture)
+ {
+ Fixture = fixture;
+ }
+
+ [Fact]
+ public async Task When_UserSearchesWithValidParameters_Then_ReturnListProperly()
+ {
+ // arrange
+ var complexParameter = new UserSearchModel
+ {
+ Username = "Bret",
+ };
+ var serialisedComplexParameters = JsonConvert.SerializeObject(complexParameter);
+ var expectedResponse = new List()
+ {
+ "Leanne Graham"
+ };
+
+ var client = Fixture.Server.CreateClient();
+ client.CreateSession();
+ var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/UserApi/Real_Responses/Happy/200_SearchListUsers.txt");
+
+ client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(@$"{Url}?userSearchModel={serialisedComplexParameters}"), response);
+
+ // act
+ var httpResponse = await client.GetAsync("/api/users");
+
+ using (new AssertionScope())
+ {
+ // assert logs
+ var logs = client.GetSessionLogs();
+ logs.Should().BeEmpty();
+
+ // assert outgoing
+ var outgoingRequests = client.GetSessionOutgoingRequests();
+ outgoingRequests.Count.Should().Be(1);
+ outgoingRequests[0].GetEndpoint().Should().Be($"GET {Url}");
+ outgoingRequests[0].GetHeaderValue("Referer").Should().Be(MovieProject.Logic.Constants.Website);
+
+ // assert return
+ httpResponse.StatusCode.Should().Be(HttpStatusCode.OK);
+
+ var list = await httpResponse.ReadJsonBody>();
+ list.Count.Should().Be(1);
+ list.Should().BeEquivalentTo(expectedResponse);
+ }
+ }
+ }
+}
diff --git a/Examples/MovieProject/MovieProject.Web/Controllers/UserController.cs b/Examples/MovieProject/MovieProject.Web/Controllers/UserController.cs
index ad34163..e5fee72 100644
--- a/Examples/MovieProject/MovieProject.Web/Controllers/UserController.cs
+++ b/Examples/MovieProject/MovieProject.Web/Controllers/UserController.cs
@@ -2,6 +2,9 @@
using MovieProject.Logic;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MovieProject.Logic.DTO.MovieProject.Logic.Proxy.DTO;
+using MovieProject.Logic.Extensions;
+using MovieProject.Logic.Service;
namespace MovieProject.Web.Controllers
{
@@ -22,5 +25,13 @@ public async Task> GetUsers()
// second controller with separate dependency used for testing SystemTestingTools
return await _userService.GetUsers();
}
+
+ [HttpPost("searchUsers")]
+ public async Task> GetSearchUsers([FromBody] UserSearchModel searchModel)
+ {
+ var proxyDtoSearchModel = searchModel.MapUserSearchModelDtoToProxyDto();
+ // second controller with separate dependency used for testing SystemTestingTools
+ return await _userService.GetSearchUsers(proxyDtoSearchModel);
+ }
}
}