diff --git a/src/Chapter20.Tests/BaseProgramTests.cs b/src/Chapter20.Tests/BaseProgramTests.cs index a97d35660..dd2cadcae 100644 --- a/src/Chapter20.Tests/BaseProgramTests.cs +++ b/src/Chapter20.Tests/BaseProgramTests.cs @@ -7,17 +7,17 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Tests; -abstract public class BaseProgramTests +public abstract class BaseProgramTests { [NotNull] - static public ProgramWrapper? ProgramWrapper { get; set; } + public static ProgramWrapper? ProgramWrapper { get; set; } protected abstract string DefaultUrl { get; } [NotNull] public TestContext? TestContext { get; set; } - [TestInitializeAttribute] + [TestInitialize] public void TestInitialize() { Assert.IsNotNull(TestContext); @@ -29,7 +29,7 @@ public void TestInitialize() [TestMethod] [DataRow("IntelliTect", @"[1-9]\d*")] [DataRow("Text Snippet That Does Not Exist On The Page", @"0")] - public void Main_FindText_VerifyOccurenceCount(string findText, string countPattern) + public void Main_FindText_VerifyOccurrenceCount(string findText, string countPattern) { string url = DefaultUrl; string expected = @@ -41,10 +41,7 @@ public void Main_FindText_VerifyOccurenceCount(string findText, string countPatt Regex.Escape(url) }'\.\s+"; string actual = IntelliTect.TestTools.Console.ConsoleAssert.Execute("", - () => - { - ProgramWrapper.Main(new string[] { findText }).Wait(); - }); + () => ProgramWrapper.Main(new string[] { findText }).Wait()); Regex regex = new(expected); Assert.IsTrue(regex.Match(actual).Success, @@ -110,78 +107,22 @@ protected static void AssertAggregateExceptionType(string messagePrefix, Aggrega }); } - [TestMethod] - [ExpectedException(typeof(System.ArgumentNullException))] - public async Task Main_GivenNullUri_ThrowException() - { - try - { - await ProgramWrapper.Main(new string[] { "irrelevant", null! }); - Assert.Fail("Expected exception was not thrown."); - } - catch (AggregateException exception) - { - if (exception.InnerExceptions.Count != 1) - { - throw new InvalidOperationException("Unexpected scenario with there being more than one inner exception."); - } - exception.Handle(innerException => - { - // Rethrowing rather than using - // if condition on the type - ExceptionDispatchInfo.Capture( - innerException!).Throw(); - - return true; - }); - } - } + protected static HttpClient GetMockedHttpClient() + => new HttpClient(new MockHttpClientHandler()); +} - [TestMethod] - [DataRow("Bad Uri", "Could not find file *")] - [DataRow("https://bad uri", "The filename, directory name, or volume label syntax is incorrect. *", "Could not find a part of the path*", "Could not find a part of the path*")] - [DataRow("https://thisisanotherbadurlthatpresumablyldoesnotexist.notexist", "No such host is known.*", "nodename nor servname provided, or not known*", "Name or service not known*")] - [ExpectedException(typeof(WebException))] - public async Task Main_GivenBadUri_ThrowException(string uri, string defaultMessagePrefix, string? messagePrefixOSX = null, string? messagePrefixLinux = null) +file class MockHttpClientHandler : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - messagePrefixOSX ??= defaultMessagePrefix; - messagePrefixLinux ??= defaultMessagePrefix; - - try - { - await ProgramWrapper.Main(new string[] { "irrelevant", uri }); - Assert.Fail("Expected exception was not thrown."); - } - catch (Exception exception) + HttpResponseMessage response = new(HttpStatusCode.OK) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - AssertMainException(defaultMessagePrefix, exception); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - AssertMainException(messagePrefixOSX, exception); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - AssertMainException(messagePrefixLinux, exception); - } - if (exception is AggregateException aggregateException) - { - // InnerException expected to be WebException. - exception = aggregateException.Flatten(); - aggregateException.Handle(innerException => - { - // Rethrowing rather than using - // if condition on the type - ExceptionDispatchInfo.Capture( - innerException) - .Throw(); - return true; - }); - } - // WebException expected - else throw; - } + Content = new StringContent($""" + Test Data From {GetType().Namespace}.{nameof(MockHttpClientHandler)} + Request Uri: {request.RequestUri}, + The tests search for the word "IntelliTect" so that is can count the number of times it appears on the page. + """) + }; + return Task.FromResult(response); } } diff --git a/src/Chapter20.Tests/Listing20.01.Tests.cs b/src/Chapter20.Tests/Listing20.01.Tests.cs index b7d41bda8..71fa08666 100644 --- a/src/Chapter20.Tests/Listing20.01.Tests.cs +++ b/src/Chapter20.Tests/Listing20.01.Tests.cs @@ -9,6 +9,7 @@ public class ProgramTests : BaseProgramTests [ClassInitialize] static public void ClassInitialize(TestContext _) { + Program.HttpClient = GetMockedHttpClient(); ProgramWrapper = new ProgramWrapper( (args) => Task.Run(() => Program.Main(args))); diff --git a/src/Chapter20.Tests/Listing20.02.Tests.cs b/src/Chapter20.Tests/Listing20.02.Tests.cs index 580df5444..c4bfc31ca 100644 --- a/src/Chapter20.Tests/Listing20.02.Tests.cs +++ b/src/Chapter20.Tests/Listing20.02.Tests.cs @@ -9,6 +9,7 @@ public class ProgramTests : BaseProgramTests [ClassInitialize] public static void ClassInitialize(TestContext _) { + Program.HttpClient = GetMockedHttpClient(); ProgramWrapper = new ProgramWrapper( (args) => Task.Run(() => Program.Main(args))); diff --git a/src/Chapter20.Tests/Listing20.03.Tests.cs b/src/Chapter20.Tests/Listing20.03.Tests.cs index 624441c81..1934faf2e 100644 --- a/src/Chapter20.Tests/Listing20.03.Tests.cs +++ b/src/Chapter20.Tests/Listing20.03.Tests.cs @@ -9,6 +9,7 @@ public class ProgramTests : BaseProgramTests [ClassInitialize] public static void ClassInitialize(TestContext _) { + Program.HttpClient = GetMockedHttpClient(); ProgramWrapper = new ProgramWrapper( (args) => Task.Run(() => Program.Main(args))); diff --git a/src/Chapter20/Listing20.01.SynchronousWebRequestHighLatency.cs b/src/Chapter20/Listing20.01.SynchronousWebRequestHighLatency.cs index 786fb1f81..f17676e95 100644 --- a/src/Chapter20/Listing20.01.SynchronousWebRequestHighLatency.cs +++ b/src/Chapter20/Listing20.01.SynchronousWebRequestHighLatency.cs @@ -7,6 +7,7 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_01; public static class Program { + public static HttpClient HttpClient { get; set; } = new(); public const string DefaultUrl = "https://IntelliTect.com"; public static void Main(string[] args) @@ -28,9 +29,8 @@ public static void Main(string[] args) $"Searching for '{findText}' at URL '{url}'."); Console.WriteLine("Downloading..."); - using WebClient webClient = new(); byte[] downloadData = - webClient.DownloadData(url); + HttpClient.GetByteArrayAsync(url).Result; Console.WriteLine("Searching..."); int textOccurrenceCount = CountOccurrences( diff --git a/src/Chapter20/Listing20.02.AsynchronousWebRequest.cs b/src/Chapter20/Listing20.02.AsynchronousWebRequest.cs index 432cffe2c..6ec176246 100644 --- a/src/Chapter20/Listing20.02.AsynchronousWebRequest.cs +++ b/src/Chapter20/Listing20.02.AsynchronousWebRequest.cs @@ -9,6 +9,7 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_02; public static class Program { + public static HttpClient HttpClient { get; set; } = new(); public const string DefaultUrl = "https://IntelliTect.com"; public static void Main(string[] args) @@ -29,9 +30,8 @@ public static void Main(string[] args) Console.WriteLine( $"Searching for '{findText}' at URL '{url}'."); - using WebClient webClient = new(); Console.Write("Downloading..."); - Task task = webClient.DownloadDataTaskAsync(url) + Task task = HttpClient.GetByteArrayAsync(url) .ContinueWith(antecedent => { byte[] downloadData = antecedent.Result; diff --git a/src/Chapter20/Listing20.03.AsynchronousHighLatencyUsingTaskAsynchronyPattern.cs b/src/Chapter20/Listing20.03.AsynchronousHighLatencyUsingTaskAsynchronyPattern.cs index fc9eb864a..d0df717f3 100644 --- a/src/Chapter20/Listing20.03.AsynchronousHighLatencyUsingTaskAsynchronyPattern.cs +++ b/src/Chapter20/Listing20.03.AsynchronousHighLatencyUsingTaskAsynchronyPattern.cs @@ -3,11 +3,11 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_03; #region INCLUDE using System; using System.IO; -using System.Net; using System.Threading.Tasks; public static class Program { + public static HttpClient HttpClient { get; set; } = new(); public const string DefaultUrl = "https://IntelliTect.com"; public static async Task Main(string[] args) @@ -28,9 +28,8 @@ public static async Task Main(string[] args) Console.WriteLine( $"Searching for '{findText}' at URL '{url}'."); - using WebClient webClient = new(); Task taskDownload = - webClient.DownloadDataTaskAsync(url); + HttpClient.GetByteArrayAsync(url); Console.Write("Downloading..."); while (!taskDownload.Wait(100))