.Net Core 2.1 TDD HttpClient and Http Requests Testing

.Net Core TDD – HttpClient and Http Requests Testing

Note: Using .net core 2.1

Using XUnit, but the concepts will remain the same no matter what testing framework you use.

Using Moq:

https://github.com/Moq/moq4/wiki/Quickstart

https://documentation.help/Moq/

There is also a really great free moq course here:

https://www.udemy.com/moq-framework

Tip:

Become familiar with Dependency Injection and Inversion Control in code so you can mock behaviour in tests.
Then become familiar with mocking in tests and assert behaviour based on mocked data or methods.

You can see working code here:

Github code:

https://github.com/CariZa/XUnit-Test-Samples

https://github.com/CariZa/XUnit-Test-Samples/blob/master/HTTPRequestsTests/RequestsTests.cs

HTTP Testing

Make use of dependency injection and inversion of control by creating a, HttpClient Handler.

HttpClient Handler

This handler will allow us to mock the behaviour of the HttpClient when we write out tests.

Interface (IHttpClientHandler.cs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace HTTPRequests
{
    public interface IHttpClientHandler
    {
        HttpResponseMessage Get(string url);
        HttpResponseMessage Post(string url, HttpContent content);
        Task<HttpResponseMessage> GetAsync(string url);
        Task<HttpResponseMessage> PostAsync(string url, HttpContent content);
    }
}

Class (HttpClientHandler.cs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace HTTPRequests
{
    public class HttpClientHandler : IHttpClientHandler
    {
        private HttpClient _client = new HttpClient();

        public HttpResponseMessage Get(string url)
        {
            return GetAsync(url).Result;
        }

        public async Task<HttpResponseMessage> GetAsync(string url)
        {
            return await _client.GetAsync(url);
        }

        public HttpResponseMessage Post(string url, HttpContent content)
        {
            return PostAsync(url, content).Result;
        }

        public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
        {
            return await _client.PostAsync(url, content);
        }
    }
}

Requests

Create a Requests class for your http requests and inject the handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace HTTPRequests
{
    public class Requests
    {
        private readonly IHttpClientHandler _httpClient;

        public Requests(IHttpClientHandler httpClient) {
            _httpClient = httpClient;
        }

        public async Task<string> GetData(string baseUrl)
        {

            IHttpClientHandler client = _httpClient;
     
            using (HttpResponseMessage res = await client.GetAsync(baseUrl))
            try
            {
                using (HttpContent content = res.Content)
                {
                    string data = await content.ReadAsStringAsync();
                    if (data != null)
                    {
                        return data;
                    }
                    else
                    {
                        return "err no data";
                    }
                }
            }
            catch (Exception e)
            {
                return "err no content";
            }

        }

        public async Task<List<TodoModel>> GetTodosByUserId(string url, int userId)
        {
            var task = GetData(url);

            List<TodoModel> todos = null;
            await task.ContinueWith((jsonString) =>
              {
                  todos = JsonConvert.DeserializeObject<List<TodoModel>>(jsonString.Result);
                  todos = todos.FindAll(x => x.userId == userId);
              });
            return todos;
        }
    }
}

Use it in your code:

Using the above Requests class in action:

1
2
3
    Requests req = new Requests(new HttpClientHandler());
    string todoItem = req.GetData("https://jsonplaceholder.typicode.com/todos/1").Result;
    List<TodoModel> todos = req.GetTodosByUserId("https://jsonplaceholder.typicode.com/todos", 1).Result;

Writing tests

Testing the Http Requests using dependency injection and inversion of control:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using HTTPRequests;
using Moq;
using Xunit;
using Newtonsoft.Json;

namespace HTTPRequestsTests
{

    public class RequestsTests //: IClassFixture<RequestsTestsFixture>
    {
        [Fact]
        public void GetData_CheckGetAsyncIsCalled()
        {
            // Arrange/Setup
            //var moqRes = new Mock<HttpResponseMessage>();
            var moqHttp = new Mock<HTTPRequests.IHttpClientHandler>();
            moqHttp.Setup(HttpHandler => HttpHandler.GetAsync(It.IsAny<string>()))
                   .Returns(() => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
            var req = new Requests(moqHttp.Object);
            var url = "testurl";

            // Act
            var todo = req.GetData(url);

            // Assert
            moqHttp.Verify(moqHttpInst => moqHttpInst.GetAsync(It.IsAny<string>()), Times.Exactly(1));
        }

        [Fact]
        public void GetData_CheckGetAsyncIsCalled_EmptyContent()
        {
            // Arrange/Setup
            var response = new HttpResponseMessage( HttpStatusCode.OK );
            var moqHttp = new Mock<HTTPRequests.IHttpClientHandler>();
            moqHttp.Setup(HttpHandler => HttpHandler.GetAsync(It.IsAny<string>()))
                   .Returns(() => Task.FromResult(response) );
            var req = new Requests(moqHttp.Object);
            var url = "testurl";

            // Act
            var todo = req.GetData(url);
            Console.WriteLine("Ending here after expection");

            // Assert
            Assert.True(todo.IsCompleted);

        }

        [Fact]
        public void GetData_CheckGetAsyncIsCalled_ReturnsString()
        {
            // Arrange/Setup

            // Content = new StringContent(SerializedString, System.Text.Encoding.UTF8, "application/json")
            var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Content here 123") };
            var moqHttp = new Mock<HTTPRequests.IHttpClientHandler>();
            moqHttp.Setup(HttpHandler => HttpHandler.GetAsync(It.IsAny<string>()))
                   .Returns(() => Task.FromResult(response));
            var req = new Requests(moqHttp.Object);
            var url = "testurl";

            // Act
            var todo = req.GetData(url);
            Console.WriteLine(todo.Result);

            //
            Assert.Equal("Content here 123", todo.Result);
        }

        [Fact]
        public void GetData_CheckGetAsyncIsCalled_ReturnsJSON()
        {
            // Arrange/Setup
            var mockJson = "{"GroupId":1,"Samples":[{"SampleId":1},{"SampleId":2}]}";
            var JSONContent = new StringContent(mockJson, System.Text.Encoding.UTF8, "application/json");
            var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = JSONContent };
            var moqHttp = new Mock<HTTPRequests.IHttpClientHandler>();
            moqHttp.Setup(HttpHandler => HttpHandler.GetAsync(It.IsAny<string>()))
                   .Returns(() => Task.FromResult(response));
            var req = new Requests(moqHttp.Object);
            var url = "testurl";

            // Act
            var todo = req.GetData(url);
            Console.WriteLine(todo.Result);

            // Assert
            Assert.Equal(mockJson, todo.Result);
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.