ইউনিট পরীক্ষায় এইচটিটিপিপ্লাইেন্টকে উপহাস করা


110

আমার কোডটি ইউনিট পরীক্ষায় ব্যবহার করার জন্য মোড়ানোর চেষ্টা করার কিছু সমস্যা রয়েছে। বিষয়গুলি এই। আমার ইন্টারফেসটি আইএইচটিপিহ্যান্ডলার রয়েছে:

public interface IHttpHandler
{
    HttpClient client { get; }
}

এবং এটি ব্যবহার করে ক্লাস, এইচটিপিহ্যান্ডলার:

public class HttpHandler : IHttpHandler
{
    public HttpClient client
    {
        get
        {
            return new HttpClient();
        }
    }
}

এবং তারপরে সংযোগ শ্রেণি, যা ক্লায়েন্ট বাস্তবায়নের জন্য ইনজেক্ট করতে সরল IOC ব্যবহার করে:

public class Connection
{
    private IHttpHandler _httpClient;

    public Connection(IHttpHandler httpClient)
    {
        _httpClient = httpClient;
    }
}

এবং তারপরে আমার একটি ইউনিট পরীক্ষার প্রকল্প রয়েছে যার এই ক্লাসটি রয়েছে:

private IHttpHandler _httpClient;

[TestMethod]
public void TestMockConnection()
{
    var client = new Connection(_httpClient);

    client.doSomething();  

    // Here I want to somehow create a mock instance of the http client
    // Instead of the real one. How Should I approach this?     

}

এখন স্পষ্টতই আমার সংযোগ শ্রেণিতে এমন পদ্ধতি থাকবে যা আমার পিছনের দিক থেকে ডেটা (জেএসএন) পুনরুদ্ধার করবে। যাইহোক, আমি এই শ্রেণীর জন্য ইউনিট পরীক্ষা লিখতে চাই এবং স্পষ্টতই আমি আসল পিছনের অংশের বিরুদ্ধে পরীক্ষা লিখতে চাই না, বরং একটি বিদ্রূপযুক্ত। আমি দুর্দান্ত সাফল্য ছাড়াই এর একটি ভাল উত্তর গুগল করার চেষ্টা করেছি। আমি মক এর আগে বিদ্রূপ করার জন্য এবং ব্যবহার করতে পারি, তবে কখনও কখনও এইচএসপিপ্লায়েন্টের মতো নয় on আমার কীভাবে এই সমস্যার সাথে যোগাযোগ করা উচিত?

আগাম ধন্যবাদ.


1
HttpClientআপনার ইন্টারফেসে এক্সপোজ করা সমস্যাটি যেখানে। আপনি আপনার ক্লায়েন্টকে HttpClientকংক্রিটের ক্লাস ব্যবহার করতে বাধ্য করছেন are পরিবর্তে, আপনি একটি এক্সপোজ করা উচিত বিমূর্ততা এর HttpClient
মাইক ইজন

আপনি কি আরও কিছুটা গভীরতার সাথে ব্যাখ্যা করতে পারেন? সংযোগ শ্রেণীর কনস্ট্রাক্টরটি কীভাবে তৈরি করব তা আমি সংযুক্তি ক্লাসের অন্যান্য ক্লাসে এইচটিপিপ্লিয়েন্টের কোনও নির্ভরতা চাই না। উদাহরণস্বরূপ, আমি সংযোগের কনস্ট্রাক্টারে কনট্রিট এইচটিটিপিপ্লিনেন্টটি পাস করতে চাই না কারণ এটি এমন প্রতিটি অন্যান্য শ্রেণি তৈরি করবে যা এইচটিটিপিপ্লিনেন্টের সংযোগ নির্ভর করে?
tjug করুন

আগ্রহের বাইরে আপনি কী গুগল করলেন? দৃশ্যত mockhttp কিছু SEO উন্নতি ব্যবহার করতে পারে।
রিচার্ড জাযায়াল

@ মাইক - যেমনটি আমার উত্তরে উল্লিখিত হয়েছে, সত্যিই এইচটিপিপ্লিয়েন্ট বিমূর্ত করার দরকার নেই। এটি যেমনটি ঠিক তেমন পরীক্ষামূলক। আমার কাছে প্রচুর প্রকল্প রয়েছে যেগুলি এই পদ্ধতিটি ব্যবহার করে ব্যাকএন্ড-কম টেস্ট স্যুট রয়েছে।
রিচার্ড জাজাল 13

উত্তর:


37

আপনার ইন্টারফেসটি কংক্রিট বর্গকে উদ্ভাসিত করে HttpClient, সুতরাং যে কোনও ক্লাস যা এই ইন্টারফেসটি ব্যবহার করে এটি এর সাথে আবদ্ধ, এর অর্থ এটি উপহাস করা যায় না।

HttpClientকোনও ইন্টারফেস থেকে উত্তরাধিকারসূত্রে আসে না তাই আপনাকে নিজের লেখা লিখতে হবে। আমি সাজসজ্জার মতো নকশার পরামর্শ দিই :

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

এবং আপনার ক্লাসটি এর মতো দেখাবে:

public class HttpClientHandler : IHttpHandler
{
    private HttpClient _client = new HttpClient();

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

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

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

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

এই সমস্ত ক্ষেত্রে পয়েন্টটি HttpClientHandlerএটি নিজস্ব তৈরি করে HttpClient, আপনি অবশ্যই একাধিক ক্লাস তৈরি করতে পারেন যা IHttpHandlerবিভিন্ন উপায়ে প্রয়োগ করে।

এই পদ্ধতির মূল সমস্যাটি হ'ল আপনি কার্যকরভাবে একটি ক্লাস লিখছেন যা কেবলমাত্র অন্য ক্লাসে পদ্ধতিগুলি কল করে, তবে আপনি এমন একটি শ্রেণি তৈরি করতে পারেন যা উত্তরাধিকার সূত্রে প্রাপ্ত হয় HttpClient( এনকোসির উদাহরণ দেখুন , এটি আমার চেয়ে অনেক ভাল পদ্ধতির)। জীবন যদি HttpClientএমন একটি ইন্টারফেস থাকে যা আপনি উপহাস করতে পারেন, তবে জীবনটি আরও সহজ হত un দুর্ভাগ্যক্রমে এটি না।

এই উদাহরণটি অবশ্য সোনার টিকিট নয়IHttpHandlerএখনও নামের উপর নির্ভর করে HttpResponseMessageযা System.Net.Httpনেমস্পেসের সাথে সম্পর্কিত, অতএব আপনার যদি অন্য কোনও বাস্তবায়ন প্রয়োজন হয় তবে HttpClientতাদের প্রতিক্রিয়াগুলিকে HttpResponseMessageবস্তুতে রূপান্তর করতে আপনাকে কোনও ধরণের ম্যাপিং করতে হবে । অবশ্যই এটি কেবলমাত্র একটি সমস্যা যদি আপনার একাধিক বাস্তবায়নের ব্যবহার করার দরকার হয় এর IHttpHandlerকিন্তু এটি দেখাচ্ছে না মত আপনি না তাই এটি বিশ্বের শেষ, কিন্তু আমার মনে হয় এটা কিছু।

যাইহোক, আপনি IHttpHandlerকংক্রিট HttpClientশ্রেণীর সম্পর্কে চিন্তা না করে কেবল উপহাস করতে পারেন কারণ এটি দূরে সরিয়ে দেওয়া হয়েছে।

আমি অ- অ্যাসিঙ্ক পদ্ধতিগুলি পরীক্ষা করার পরামর্শ দিই , কেননা এগুলি এখনও অ্যাসিক্রোনাস পদ্ধতিগুলি কল করে তবে ইউনিট পরীক্ষার অ্যাসিক্রোনাস পদ্ধতি সম্পর্কে চিন্তা করার ঝামেলা ছাড়াই এখানে দেখুন


এটি সত্যিই আমার প্রশ্নের উত্তর দেয়। এনকোসিস উত্তরটিও সঠিক তাই আমি কোনটি উত্তর হিসাবে গ্রহণ করব তা নিশ্চিত নই, তবে আমি এইটির সাথে যাব। প্রচেষ্টার জন্য আপনাকে উভয়কে ধন্যবাদ
tjug করুন

@tjugg সাহায্য করার জন্য খুশি। যদি আপনি সেগুলিকে দরকারী বলে মনে করেন তবে নির্দ্বিধায় জবাব দিন।
এনকোসি

3
এই উত্তর এবং এনকোসির মধ্যে প্রধান পার্থক্য এটি অনেক পাতলা বিমূর্ততা বলে মনে করা যায়। পাতলা সম্ভবত কোনও নম্র বস্তুর
বেন অ্যারনসন

227

এইচটিপিপি্লিয়েন্টের এক্সটেনসিবিলিটি কনস্ট্রাক্টরের কাছে HttpMessageHandlerপাসের মধ্যে রয়েছে । এর উদ্দেশ্য প্ল্যাটফর্ম নির্দিষ্ট প্রয়োগের অনুমতি দেওয়া, তবে আপনি এটি উপহাসও করতে পারেন। এইচটিটিপিপ্লায়েন্টের জন্য ডেকোরেটর মোড়ক তৈরি করার দরকার নেই।

আপনি যদি মোখ ব্যবহারের ক্ষেত্রে ডিএসএলকে পছন্দ করেন তবে আমার কাছে গিটহাব / নুগেটে একটি লাইব্রেরি আছে যা জিনিসগুলিকে কিছুটা সহজ করে তোলে: https://github.com/richardszalay/mockhttp

var mockHttp = new MockHttpMessageHandler();

// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localost/api/user/*")
        .Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON

// Inject the handler or client into your application code
var client = new HttpClient(mockHttp);

var response = await client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync("http://localhost/api/user/1234").Result;

var json = await response.Content.ReadAsStringAsync();

// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}

1
সুতরাং আমি মক এইচটিপিএমেসেজহ্যান্ডলারকে মেসেজহ্যান্ডার এইচটিপিএন্ডার ক্লাস হিসাবে পাস করব? অথবা আপনি কীভাবে নিজের প্রকল্পে এটা বাস্তবায়িত হয়েছে
tjugg

2
দুর্দান্ত উত্তর এবং এমন কিছু যা আমি প্রথমে জানতাম না। এইচটিপিপ্লেইয়েন্টের সাথে কাজ করা এত খারাপ নয়।
বিলার

6
যে ব্যক্তিরা ক্লায়েন্টকে ইনজেকশন দেওয়ার সাথে মোকাবেলা করতে চান না তবে এখনও সহজে টেস্টাবিলিটি চান, এটি অর্জন তুচ্ছ। কেবল একটি ক্ষেত্রের var client = new HttpClient()সাথে প্রতিস্থাপন var client = ClientFactory()এবং সেটআপ করুন internal static Func<HttpClient> ClientFactory = () => new HttpClient();এবং পরীক্ষার স্তরে আপনি এই ক্ষেত্রটি পুনরায় লিখতে পারেন।
ক্রিস মেরিসিক

3
@ ক্রিসমারিসিক আপনি ইঞ্জেকশন প্রতিস্থাপনের জন্য পরিষেবা ফর্মের একটি ফর্মের পরামর্শ দিচ্ছেন। পরিষেবার অবস্থানটি একটি সুপরিচিত অ্যান্টি-প্যাটার্ন তাই ইমো ইঞ্জেকশন পছন্দনীয়।
মারিওডিএস

2
@MarioDS এবং নির্বিশেষে, আপনি একটি HttpClient ইনজেকশনের করা উচিত নয় উদাহরণস্বরূপ এ সব। আপনি এই তারপর আপনি একটি ইনজেকশনের করা উচিত জন্য constructer ইনজেকশন ব্যবহার করে দৃঢতা হন HttpClientFactoryহিসেবে Func<HttpClient>। আমি এইচটিপিপ্লিয়েন্টকে নিখুঁতভাবে প্রয়োগের বিবরণ হিসাবে দেখছি এবং নির্ভরতা নয়, আমি উপরের চিত্রের মতোই স্ট্যাটিক ব্যবহার করব। আমি অভ্যন্তরীণ পরীক্ষাগুলি সম্পূর্ণরূপে ভাল। যদি আমি খাঁটি-ইসম সম্পর্কে চিন্তা করি তবে আমি পুরো সার্ভারগুলি দাঁড়াব এবং লাইভ কোড পাথ পরীক্ষা করব। কোনও ধরণের উপহাস ব্যবহারের অর্থ আপনি আসল আচরণ নয় বরং আচরণের সান্নিধ্য গ্রহণ করতে পারেন।
ক্রিস মেরিসিক

39

আমি অন্য কয়েকটি উত্তরের সাথে একমত হয়েছি যে এইচটিপিপিলেয়েন্ট মোড়ানোর পরিবর্তে সবচেয়ে ভাল পন্থা এইচটিপিএমেসেজহ্যান্ডলারকে বিদ্রূপ করা। এই উত্তরটি অনন্য যে এটি এখনও এইচটিপিপ্লিয়েন্টকে ইনজেকশন দেয়, একে একে সিঙ্গলটন হতে দেয় বা নির্ভরতা ইনজেকশন দিয়ে পরিচালিত হয়।

"এইচটিপিপি্লিয়েন্ট একবার ইনস্ট্যান্ট করা এবং অ্যাপ্লিকেশনটির পুরো জীবন জুড়ে পুনরায় ব্যবহার করার উদ্দেশ্যে" " ( উত্স )

এইচটিপিএমেসেজহ্যান্ডলারকে উপহাস করা কিছুটা জটিল হতে পারে কারণ সেন্ডএন্সিঙ্ক সুরক্ষিত। Xunit এবং Moq ব্যবহার করে এখানে একটি সম্পূর্ণ উদাহরণ।

using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
using Xunit;
// Use nuget to install xunit and Moq

namespace MockHttpClient {
    class Program {
        static void Main(string[] args) {
            var analyzer = new SiteAnalyzer(Client);
            var size = analyzer.GetContentSize("http://microsoft.com").Result;
            Console.WriteLine($"Size: {size}");
        }

        private static readonly HttpClient Client = new HttpClient(); // Singleton
    }

    public class SiteAnalyzer {
        public SiteAnalyzer(HttpClient httpClient) {
            _httpClient = httpClient;
        }

        public async Task<int> GetContentSize(string uri)
        {
            var response = await _httpClient.GetAsync( uri );
            var content = await response.Content.ReadAsStringAsync();
            return content.Length;
        }

        private readonly HttpClient _httpClient;
    }

    public class SiteAnalyzerTests {
        [Fact]
        public async void GetContentSizeReturnsCorrectLength() {
            // Arrange
            const string testContent = "test content";
            var mockMessageHandler = new Mock<HttpMessageHandler>();
            mockMessageHandler.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent(testContent)
                });
            var underTest = new SiteAnalyzer(new HttpClient(mockMessageHandler.Object));

            // Act
            var result = await underTest.GetContentSize("http://anyurl");

            // Assert
            Assert.Equal(testContent.Length, result);
        }
    }
}

1
আমি সত্যিই এটি পছন্দ। mockMessageHandler.Protected()হত্যাকারী ছিলেন। এই উদাহরণের জন্য ধন্যবাদ। এটি উত্সটি কোনওরকম পরিবর্তন না করে পরীক্ষা লেখার অনুমতি দেয়।
tyrion

1
এফওয়াইআই, মোখ ৪.৮ সুরক্ষিত সদস্যদের জোরালোভাবে টাইপ করা উপহাসের সমর্থন করে - github.com/Moq/moq4/wiki/Quickstart
রিচার্ড

2
এটি দুর্দান্ত দেখাচ্ছে। এছাড়াও MOQ ReturnsAsync তাই কোডের মত দেখাবে সমর্থন.ReturnsAsync(new HttpResponseMessage {StatusCode = HttpStatusCode.OK, Content = new StringContent(testContent)})
Kord

ধন্যবাদ @ কর্ড, আমি উত্তরে এটি যোগ করেছি
পয়েন্টজিরোটো

3
"SandAsync" কে কিছু পরামিতি সহ ডাকা হয়েছিল তা যাচাই করার কোনও উপায় আছে কি? আমি ব্যবহার করার চেষ্টা করেছি ... সুরক্ষিত ()। যাচাই করুন (...), তবে দেখে মনে হচ্ছে এটি অ্যাসিঙ্ক পদ্ধতিতে কাজ করে না।
রোমান

29

এটি একটি সাধারণ প্রশ্ন, এবং আমি এইচটিপিপ্লেইয়েন্টকে উপহাস করার ক্ষমতাটি চেয়ে খুব বেশি পক্ষে ছিলাম, তবে আমি মনে করি অবশেষে আমি বুঝতে পেরেছি যে আপনি এইচটিপিপ্লিয়েন্টকে উপহাস করবেন না। এটি করা যৌক্তিক বলে মনে হয় তবে আমি মনে করি ওপেন সোর্স লাইব্রেরিতে আমরা যে জিনিসগুলি দেখি সেগুলি দ্বারা আমরা ব্রেইন ওয়াশ হয়েছি।

আমরা প্রায়শই সেখানে "ক্লায়েন্টদের" দেখতে পাই যে আমরা আমাদের কোডটিতে উপহাস করি যাতে আমরা বিচ্ছিন্নতার সাথে পরীক্ষা করতে পারি, তাই আমরা স্বয়ংক্রিয়ভাবে একই নীতিটি HTTPClient এ প্রয়োগ করার চেষ্টা করি। এইচটিটিপিপ্লিয়েন্ট আসলে অনেক কিছু করে; আপনি এটিকে HTTPMessageHandler এর পরিচালক হিসাবে ভাবতে পারেন, সুতরাং আপনি এটি উপহাস করতে চান না এবং এজন্য এখনও এর কোনও ইন্টারফেস নেই। আপনি যে অংশটি ইউনিট পরীক্ষার জন্য বা আপনার পরিষেবাগুলি ডিজাইনের জন্য সত্যই আগ্রহী তা হ'ল HTTPMessageHandler যেহেতু সেই প্রতিক্রিয়া ফিরিয়ে দেয় এবং আপনি এটি উপহাস করতে পারেন

এটি সম্ভবত এটিও উল্লেখযোগ্য যে আপনার সম্ভবত এইচটিপিপ্লেইয়েন্টকে আরও বড় চুক্তির মতো চিকিত্সা করা শুরু করা উচিত। উদাহরণস্বরূপ: আপনার নতুন এইচটিপিপিপ্লায়েন্টদের সর্বনিম্ন রাখুন। তাদের পুনরায় ব্যবহার করুন, এগুলি পুনরায় ব্যবহারের জন্য তৈরি করা হয়েছে এবং যদি আপনি করেন তবে ক্রেপ টন কম সংস্থান ব্যবহার করুন। আপনি যদি এটি আরও বড় চুক্তির মতো চিকিত্সা করা শুরু করেন তবে এটি উপহাস করার ইচ্ছায় এটি আরও অনেক বেশি খারাপ অনুভব করবে এবং এখন মেসেজ হ্যান্ডলারটি ক্লায়েন্ট নয় বরং আপনি যে জিনিসটি ইনজেকশন দিচ্ছেন তা হতে শুরু করবে।

অন্য কথায়, ক্লায়েন্টের পরিবর্তে হ্যান্ডলারের চারপাশে আপনার নির্ভরতাগুলি ডিজাইন করুন। আরও ভাল, বিমূর্ত "পরিষেবাগুলি" যা HTTPClient ব্যবহার করে যা আপনাকে কোনও হ্যান্ডলারের ইনজেক্ট করতে দেয় এবং পরিবর্তে এটি আপনার ইনজেক্টেবল নির্ভরতা হিসাবে ব্যবহার করে। তারপরে আপনার পরীক্ষাগুলিতে, আপনি নিজের পরীক্ষাগুলি সেট আপ করার জন্য প্রতিক্রিয়া নিয়ন্ত্রণ করতে হ্যান্ডলারটিকে নকল করতে পারেন।

এইচটিপিএলপ্লেইন্টের মোড়ানো সময় ব্যতীত অপচয়।

আপডেট: জোশুয়া ডুমসের উদাহরণ দেখুন। আমি ঠিক এটিই প্রস্তাব করছি।


17

মন্তব্য উল্লেখ হিসাবে আপনি প্রয়োজন বিমূর্ত দূরে HttpClientতাই হিসাবে এটি মিলিত করা হবে না। আমি অতীতেও তেমন কিছু করেছি। আপনি যা করার চেষ্টা করছেন তার সাথে আমি যা করেছি তা খাপ খাইয়ে নেওয়ার চেষ্টা করব।

প্রথম HttpClientশ্রেণীর দিকে নজর দিন এবং এটি কী কার্যকারিতা সরবরাহ করবে তা প্রয়োজনীয় হবে।

এখানে একটি সম্ভাবনা:

public interface IHttpClient {
    System.Threading.Tasks.Task<T> DeleteAsync<T>(string uri) where T : class;
    System.Threading.Tasks.Task<T> DeleteAsync<T>(Uri uri) where T : class;
    System.Threading.Tasks.Task<T> GetAsync<T>(string uri) where T : class;
    System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class;
    System.Threading.Tasks.Task<T> PostAsync<T>(string uri, object package);
    System.Threading.Tasks.Task<T> PostAsync<T>(Uri uri, object package);
    System.Threading.Tasks.Task<T> PutAsync<T>(string uri, object package);
    System.Threading.Tasks.Task<T> PutAsync<T>(Uri uri, object package);
}

আবার যেমনটি আগে বলা হয়েছিল তা ছিল বিশেষ উদ্দেশ্যে। আমি যে কোনও ব্যবসায়ের সাথে সবচেয়ে বেশি নির্ভরতা পুরোপুরি বিমুগ্ধ করেছিলাম HttpClientএবং আমি কী ফিরিয়ে দিতে চাই তাতে মনোনিবেশ করেছি। আপনার HttpClientকেবলমাত্র প্রয়োজনীয় কার্যকারিতা সরবরাহ করতে আপনি কীভাবে বিমূর্ত করতে চান তা মূল্যায়ন করা উচিত ।

এটি এখন আপনাকে যা পরীক্ষা করার প্রয়োজন তা কেবল বিদ্রূপ করার অনুমতি দেবে।

এমনকি আমি IHttpHandlerসম্পূর্ণরূপে দূরে সরে যাওয়ার এবং HttpClientবিমূর্ততাটি ব্যবহার করার পরামর্শ দেব IHttpClient। তবে আমি কেবল বাছাই করছি না কারণ আপনি বিমূর্ত ক্লায়েন্টের সদস্যদের সাথে আপনার হ্যান্ডলার ইন্টারফেসের বডি প্রতিস্থাপন করতে পারেন।

এর বাস্তবায়নের পরে সেই IHttpClientবিষয়টির জন্য একটি আসল / কংক্রিট HttpClientবা অন্য কোনও অবজেক্টকে গুছিয়ে / মানিয়ে নিতে ব্যবহার করা যেতে পারে, এটি HTTP রিকোয়েস্টগুলি করতে ব্যবহার করা যেতে পারে কারণ আপনি যা চেয়েছিলেন তা হ'ল একটি পরিষেবা যা সেই কার্যকারিতা সরবরাহ করেছিল যা HttpClientবিশেষভাবে প্রয়োগ হয়েছিল । বিমূর্তনটি ব্যবহার করা একটি পরিষ্কার (আমার মতামত) এবং সলিউড পদ্ধতির এবং ফ্রেমওয়ার্ক পরিবর্তনের সাথে সাথে যদি অন্য কোনও কারণে অন্তর্নিহিত ক্লায়েন্টকে স্যুইচ আউট করার প্রয়োজন হয় তবে আপনার কোডটি আরও রক্ষণাবেক্ষণযোগ্য করে তুলতে পারে।

বাস্তবায়ন কীভাবে করা যায় তার একটি স্নিপেট এখানে।

/// <summary>
/// HTTP Client adaptor wraps a <see cref="System.Net.Http.HttpClient"/> 
/// that contains a reference to <see cref="ConfigurableMessageHandler"/>
/// </summary>
public sealed class HttpClientAdaptor : IHttpClient {
    HttpClient httpClient;

    public HttpClientAdaptor(IHttpClientFactory httpClientFactory) {
        httpClient = httpClientFactory.CreateHttpClient(**Custom configurations**);
    }

    //...other code

     /// <summary>
    ///  Send a GET request to the specified Uri as an asynchronous operation.
    /// </summary>
    /// <typeparam name="T">Response type</typeparam>
    /// <param name="uri">The Uri the request is sent to</param>
    /// <returns></returns>
    public async System.Threading.Tasks.Task<T> GetAsync<T>(Uri uri) where T : class {
        var result = default(T);
        //Try to get content as T
        try {
            //send request and get the response
            var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
            //if there is content in response to deserialize
            if (response.Content.Headers.ContentLength.GetValueOrDefault() > 0) {
                //get the content
                string responseBodyAsText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                //desrialize it
                result = deserializeJsonToObject<T>(responseBodyAsText);
            }
        } catch (Exception ex) {
            Log.Error(ex);
        }
        return result;
    }

    //...other code
}

আপনি উপরের উদাহরণে দেখতে পাচ্ছেন, সাধারণত ব্যবহারের সাথে যুক্ত প্রচুর ভারী উত্তোলন HttpClientবিমূর্তির পিছনে লুকানো থাকে।

আপনার সংযোগ ক্লাসটি বিমূর্ত ক্লায়েন্টের সাথে ইনজেক্ট করা যেতে পারে

public class Connection
{
    private IHttpClient _httpClient;

    public Connection(IHttpClient httpClient)
    {
        _httpClient = httpClient;
    }
}

আপনার পরীক্ষা তারপরে আপনার SUT এর জন্য যা প্রয়োজন তা উপহাস করতে পারে

private IHttpClient _httpClient;

[TestMethod]
public void TestMockConnection()
{
    SomeModelObject model = new SomeModelObject();
    var httpClientMock = new Mock<IHttpClient>();
    httpClientMock.Setup(c => c.GetAsync<SomeModelObject>(It.IsAny<string>()))
        .Returns(() => Task.FromResult(model));

    _httpClient = httpClientMock.Object;

    var client = new Connection(_httpClient);

    // Assuming doSomething uses the client to make
    // a request for a model of type SomeModelObject
    client.doSomething();  
}

এই উত্তর। উপরে একটি বিমূর্ততা HttpClientএবং আপনার নির্দিষ্ট উদাহরণটি ব্যবহার করে তৈরি করতে একটি অ্যাডাপ্টার HttpClientFactory। এটি করার ফলে এইচটিটিপি অনুরোধের তুচ্ছ ছাড়িয়ে যুক্তি পরীক্ষা করা যায় যা এখানে লক্ষ্য।
পিমব্রউবার্স

13

অন্যান্য উত্তরগুলির উপর ভিত্তি করে, আমি এই কোডটি প্রস্তাব করি, যার কোনও বাইরের নির্ভরতা নেই:

[TestClass]
public class MyTestClass
{
    [TestMethod]
    public async Task MyTestMethod()
    {
        var httpClient = new HttpClient(new MockHttpMessageHandler());

        var content = await httpClient.GetStringAsync("http://some.fake.url");

        Assert.AreEqual("Content as string", content);
    }
}

public class MockHttpMessageHandler : HttpMessageHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var responseMessage = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent("Content as string")
        };

        return await Task.FromResult(responseMessage);
    }
}

4
আপনি কার্যকরভাবে আপনার উপহাস পরীক্ষা করছেন। মকের আসল শক্তি হ'ল আপনি প্রত্যাশা সেট করতে পারেন এবং প্রতিটি পরীক্ষায় এর আচরণ পরিবর্তন করতে পারেন। আপনাকে কিছু লোককে বাস্তবায়ন করতে হবে তা সত্যকে HttpMessageHandlerঅসম্ভব করে তোলে - এবং পদ্ধতিগুলি হওয়ায় আপনাকে তা করতে হবে protected internal
মারিওডিএস 17'17

3
@ মারিওডিডিএস আমি মনে করি মূল বিষয়টি হ'ল আপনি এইচটিটিপি প্রতিক্রিয়াটিকে উপহাস করতে পারেন যাতে আপনি বাকী কোডটি পরীক্ষা করতে পারেন। আপনি যদি এমন কোনও কারখানা ইনজেক্ট করেন যা এইচটিটিপিপ্লায়েন্ট পায় তবে পরীক্ষাগুলিতে আপনি এই এইচটিটিপি ক্লিনেন্ট সরবরাহ করতে পারেন।
chris31389

13

আমি মনে করি সমস্যাটি হ'ল আপনি এটি কিছুটা উল্টো দিকে পেয়েছেন।

public class AuroraClient : IAuroraClient
{
    private readonly HttpClient _client;

    public AuroraClient() : this(new HttpClientHandler())
    {
    }

    public AuroraClient(HttpMessageHandler messageHandler)
    {
        _client = new HttpClient(messageHandler);
    }
}

আপনি যদি উপরের ক্লাসের দিকে তাকান তবে আমার মনে হয় এটি আপনি চান। মাইক্রোসফ্ট অনুকূল কর্মক্ষমতা জন্য ক্লায়েন্টকে বাঁচিয়ে রাখার পরামর্শ দেয়, সুতরাং এই ধরণের কাঠামো আপনাকে এটি করতে দেয়। এছাড়াও HTTPMessageHandler একটি বিমূর্ত শ্রেণি এবং তাই মশকরা। আপনার পরীক্ষার পদ্ধতিটি এর পরে দেখতে হবে:

[TestMethod]
public void TestMethod1()
{
    // Arrange
    var mockMessageHandler = new Mock<HttpMessageHandler>();
    // Set up your mock behavior here
    var auroraClient = new AuroraClient(mockMessageHandler.Object);
    // Act
    // Assert
}

এটি HTTPClient এর আচরণকে উপহাস করার সময় আপনার যুক্তি পরীক্ষা করতে দেয়।

দুঃখিত বন্ধুরা, এটি লেখার পরে এবং নিজে চেষ্টা করার পরে আমি বুঝতে পেরেছি আপনি এইচটিটিপিএমেসেজহ্যান্ডলারের সুরক্ষিত পদ্ধতিগুলি উপহাস করতে পারবেন না। আমি পরবর্তী সময়ে একটি সঠিক উপহাসের ইনজেকশনের জন্য নিম্নলিখিত কোডটি যুক্ত করেছি।

public interface IMockHttpMessageHandler
{
    Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}

public class MockHttpMessageHandler : HttpMessageHandler
{
    private readonly IMockHttpMessageHandler _realMockHandler;

    public MockHttpMessageHandler(IMockHttpMessageHandler realMockHandler)
    {
        _realMockHandler = realMockHandler;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return await _realMockHandler.SendAsync(request, cancellationToken);
    }
}

এর সাথে লিখিত পরীক্ষাগুলি নীচের মত দেখতে কিছু দেখতে:

[TestMethod]
public async Task GetProductsReturnsDeserializedXmlXopData()
{
    // Arrange
    var mockMessageHandler = new Mock<IMockHttpMessageHandler>();
    // Set up Mock behavior here.
    var client = new AuroraClient(new MockHttpMessageHandler(mockMessageHandler.Object));
    // Act
    // Assert
}

9

আমার এক সহকর্মী লক্ষ্য করেছেন যে বেশিরভাগ HttpClientপদ্ধতিগুলি SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)হুডের নীচে কল করে, যা একটি ভার্চুয়াল পদ্ধতি বন্ধ রয়েছে HttpMessageInvoker:

সুতরাং উপহাস করার সহজতম উপায়টি HttpClientছিল সেই নির্দিষ্ট পদ্ধতিটিকে কেবল উপহাস করা:

var mockClient = new Mock<HttpClient>();
mockClient.Setup(client => client.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())).ReturnsAsync(_mockResponse.Object);

এবং আপনার কোডটি HttpClientনিয়মিত সহ ক্লাসের পদ্ধতিগুলিতে সর্বাধিক (তবে সমস্ত নয়) কল করতে পারে

httpClient.SendAsync(req)

Https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs নিশ্চিত করতে এখানে চেক করুন


1
এটি এমন কোনও কোডের জন্য কাজ করে না যা SendAsync(HttpRequestMessage)সরাসরি কল করে। আপনি যদি এই কোডটি সুবিধামূলক ফাংশনটি ব্যবহার না করার জন্য আপনার কোডটি সংশোধন করতে পারেন, তবে ওভাররাইড করে সরাসরি এইচটিপিপ্লেইন্টকে উপহাস SendAsyncকরা আসলে আমার কাছে পাওয়া সবচেয়ে পরিষ্কার সমাধান।
ডিলান নিকলসন

8

একটি বিকল্প হ'ল একটি স্টাব এইচটিটিপি সার্ভার সেটআপ করা হবে যা অনুরোধ ইউআরএলের সাথে মিল রেখে প্যাটার্নের ভিত্তিতে ক্যানড প্রতিক্রিয়া দেয়, যার অর্থ আপনি সত্যিকারের এইচটিটিপি অনুরোধগুলিকে মক না করে পরীক্ষা করেন। Orতিহাসিকভাবে এটি উল্লেখযোগ্যভাবে স্বচ্ছল প্রচেষ্টা গ্রহণ করতে পারত এবং ইউনিট পরীক্ষার জন্য বিবেচনা করা খুব ধীর হয়ে যেত, তবে ওএসএস লাইব্রেরি ওয়্যারমক নেট ব্যবহার করতে খুব সহজ এবং প্রচুর পরীক্ষা দিয়ে চালানোর জন্য যথেষ্ট দ্রুত তাই বিবেচনার জন্য উপযুক্ত হতে পারে। সেটআপটি কোডের কয়েকটি লাইন:

var server = FluentMockServer.Start();
server.Given(
      Request.Create()
      .WithPath("/some/thing").UsingGet()
   )
   .RespondWith(
       Response.Create()
       .WithStatusCode(200)
       .WithHeader("Content-Type", "application/json")
       .WithBody("{'attr':'value'}")
   );

পরীক্ষাগুলিতে ওয়্যারমক ব্যবহারের জন্য আপনি আরও বিশদ এবং গাইডেন্স পেতে পারেন


8

এখানে একটি সহজ সমাধান, যা আমার পক্ষে ভালভাবে কাজ করেছে।

Moq বিদ্রূপের লাইব্রেরি ব্যবহার করে।

// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
   .Protected()
   // Setup the PROTECTED method to mock
   .Setup<Task<HttpResponseMessage>>(
      "SendAsync",
      ItExpr.IsAny<HttpRequestMessage>(),
      ItExpr.IsAny<CancellationToken>()
   )
   // prepare the expected response of the mocked http call
   .ReturnsAsync(new HttpResponseMessage()
   {
      StatusCode = HttpStatusCode.OK,
      Content = new StringContent("[{'id':1,'value':'1'}]"),
   })
   .Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
   BaseAddress = new Uri("http://test.com/"),
};

var subjectUnderTest = new MyTestClass(httpClient);

// ACT
var result = await subjectUnderTest
   .GetSomethingRemoteAsync('api/test/whatever');

// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);

// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");

handlerMock.Protected().Verify(
   "SendAsync",
   Times.Exactly(1), // we expected a single external request
   ItExpr.Is<HttpRequestMessage>(req =>
      req.Method == HttpMethod.Get  // we expected a GET request
      && req.RequestUri == expectedUri // to this uri
   ),
   ItExpr.IsAny<CancellationToken>()
);

সূত্র: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/


আমি এটি সফলভাবে ব্যবহার করেছি। আমি এটিকে এখনও অ্যানোহর নুগেট নির্ভরতার মধ্যে ডিফ্র্যাগিংয়ের চেয়ে পছন্দ করি এবং আপনি হুডের নীচে কী ঘটছে সে সম্পর্কে আপনি খানিকটা আরও শিখতে পারেন। সুন্দর জিনিস হ'ল উপায়গুলি বেশিরভাগ ক্ষেত্রেই শেষ SendAsyncহয় তাই কোনও অতিরিক্ত সেটআপের প্রয়োজন হয় না।
স্টিভ পেটিফার

4

আমি অনেক উত্তর দ্বারা বিশ্বাসী না।

প্রথমত, কল্পনা করুন যে আপনি কোনও পদ্ধতিটি ব্যবহার করছেন তা পরীক্ষা করতে চান HttpClientHttpClientআপনার বাস্তবায়নে সরাসরি তাত্পর্যপূর্ণ করা উচিত নয় । আপনার জন্য একটি উদাহরণ সরবরাহ করার দায়িত্ব নিয়ে কারখানাটি ইনজেক করা উচিত HttpClient। এইভাবে আপনি পরে সেই কারখানায় বিদ্রূপ করতে পারেন এবং যে কোনওটি ফিরে আসতে HttpClientপারেন (যেমন: একটি উপহাস HttpClientএবং আসলটি নয়)।

সুতরাং, আপনার নীচের মত একটি কারখানা হবে:

public interface IHttpClientFactory
{
    HttpClient Create();
}

এবং একটি বাস্তবায়ন:

public class HttpClientFactory
    : IHttpClientFactory
{
    public HttpClient Create()
    {
        var httpClient = new HttpClient();
        return httpClient;
    }
}

অবশ্যই এই বাস্তবায়নটি আপনার আইওসি কনটেইনারটিতে নিবন্ধন করতে হবে। আপনি যদি অটোফ্যাক ব্যবহার করেন তবে এটি এমন কিছু হবে:

builder
    .RegisterType<IHttpClientFactory>()
    .As<HttpClientFactory>()
    .SingleInstance();

এখন আপনার একটি যথাযথ এবং পরীক্ষামূলক বাস্তবায়ন হবে। ভাবুন যে আপনার পদ্ধতিটি এমন কিছু:

public class MyHttpClient
    : IMyHttpClient
{
    private readonly IHttpClientFactory _httpClientFactory;

    public SalesOrderHttpClient(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<string> PostAsync(Uri uri, string content)
    {
        using (var client = _httpClientFactory.Create())
        {
            var clientAddress = uri.GetLeftPart(UriPartial.Authority);
            client.BaseAddress = new Uri(clientAddress);
            var content = new StringContent(content, Encoding.UTF8, "application/json");
            var uriAbsolutePath = uri.AbsolutePath;
            var response = await client.PostAsync(uriAbsolutePath, content);
            var responseJson = response.Content.ReadAsStringAsync().Result;
            return responseJson;
        }
    }
}

এখন পরীক্ষার অংশ। HttpClientপ্রসারিত HttpMessageHandler, যা বিমূর্ত। আসুন এর একটি "মক" তৈরি করুন HttpMessageHandlerযা একটি প্রতিনিধিকে গ্রহণ করে যাতে আমরা মকটি ব্যবহার করার সময় প্রতিটি পরীক্ষার জন্য প্রতিটি আচরণও সেটআপ করতে পারি।

public class MockHttpMessageHandler 
    : HttpMessageHandler
{
    private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _sendAsyncFunc;

    public MockHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsyncFunc)
    {
        _sendAsyncFunc = sendAsyncFunc;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return await _sendAsyncFunc.Invoke(request, cancellationToken);
    }
}

এবং এখন এবং মোকের সহায়তায় (এবং ফ্লুয়েটসেসারেশন, একটি গ্রন্থাগার যা ইউনিটকে আরও পাঠযোগ্য করে তোলে) HttpClient

public static class PostAsyncTests
{
    public class Given_A_Uri_And_A_JsonMessage_When_Posting_Async
        : Given_WhenAsync_Then_Test
    {
        private SalesOrderHttpClient _sut;
        private Uri _uri;
        private string _content;
        private string _expectedResult;
        private string _result;

        protected override void Given()
        {
            _uri = new Uri("http://test.com/api/resources");
            _content = "{\"foo\": \"bar\"}";
            _expectedResult = "{\"result\": \"ok\"}";

            var httpClientFactoryMock = new Mock<IHttpClientFactory>();
            var messageHandlerMock =
                new MockHttpMessageHandler((request, cancellation) =>
                {
                    var responseMessage =
                        new HttpResponseMessage(HttpStatusCode.Created)
                        {
                            Content = new StringContent("{\"result\": \"ok\"}")
                        };

                    var result = Task.FromResult(responseMessage);
                    return result;
                });

            var httpClient = new HttpClient(messageHandlerMock);
            httpClientFactoryMock
                .Setup(x => x.Create())
                .Returns(httpClient);

            var httpClientFactory = httpClientFactoryMock.Object;

            _sut = new SalesOrderHttpClient(httpClientFactory);
        }

        protected override async Task WhenAsync()
        {
            _result = await _sut.PostAsync(_uri, _content);
        }


        [Fact]
        public void Then_It_Should_Return_A_Valid_JsonMessage()
        {
            _result.Should().BeEquivalentTo(_expectedResult);
        }
    }
}

স্পষ্টতই এই পরীক্ষাটি নির্বোধ, এবং আমরা সত্যিই আমাদের উপহাস পরীক্ষা করছি। তবে আপনি ধারণা পেতে পারেন। আপনার প্রয়োগের উপর নির্ভর করে আপনার অর্থপূর্ণ যুক্তি পরীক্ষা করা উচিত যেমন ..

  • যদি প্রতিক্রিয়ার কোড স্থিতিটি 201 না হয় তবে এটি কি একটি ব্যতিক্রম ছুঁড়ে ফেলা উচিত?
  • প্রতিক্রিয়া পাঠ্য যদি বিশ্লেষণ করা যায় না, তবে কী হবে?
  • প্রভৃতি

এই উত্তরের উদ্দেশ্যটি হ'ল এইচটিপিপ্লিনেন্ট ব্যবহার করে এমন কিছু পরীক্ষা করা এবং এটি করার জন্য এটি একটি দুর্দান্ত পরিষ্কার উপায় clean


4

কিছুটা দেরিতে পার্টিতে যোগদান করা, তবে ডাউন স্ট্রিম আরআরএস নির্ভরতার সাথে ডটনেট কোর মাইক্রোসার্চিসমূহের সংহতকরণের ক্ষেত্রে ওয়্যারমকিং ( https://github.com/WireMock-Net/WireMock.Net ) ব্যবহার করা আমার পছন্দ হয়েছে like

আইএইচটিটিপিপ্লায়েন্টফ্যাক্টির প্রসারিত একটি টেস্টএইচটিপিপ্লায়েন্টফ্যাক্টরি প্রয়োগ করে আমরা পদ্ধতিটি ওভাররাইড করতে পারি

এইচটিপিপি্লিয়েন্ট ক্রিয়েট ক্লিয়েন্ট (স্ট্রিং নাম)

সুতরাং আপনার অ্যাপ্লিকেশনটির মধ্যে নামযুক্ত ক্লায়েন্টগুলি ব্যবহার করার সময় আপনি নিজের ওয়্যারমকটিতে তারযুক্ত কোনও HTTPClient ফেরত দেওয়ার নিয়ন্ত্রণে থাকবেন।

এই পদ্ধতির ভাল জিনিস হ'ল আপনি যা যা পরীক্ষা করছেন সেটির মধ্যে আপনি কোনও পরিবর্তন করছেন না, এবং কোর্স ইন্টিগ্রেশন পরীক্ষাগুলি সক্ষম করে আপনার পরিষেবাতে একটি সত্যিকারের REST অনুরোধ করছেন এবং জসনকে (বা যাই হোক না কেন) আসল প্রবাহের অনুরোধটি ফিরে আসবে m এটি সংক্ষিপ্ত পরীক্ষার দিকে পরিচালিত করে এবং আপনার আবেদনে যতটা সম্ভব বিদ্রূপ করা যায়।

    public class TestHttpClientFactory : IHttpClientFactory 
{
    public HttpClient CreateClient(string name)
    {
        var httpClient = new HttpClient
        {
            BaseAddress = new Uri(G.Config.Get<string>($"App:Endpoints:{name}"))
            // G.Config is our singleton config access, so the endpoint 
            // to the running wiremock is used in the test
        };
        return httpClient;
    }
}

এবং

// in bootstrap of your Microservice
IHttpClientFactory factory = new TestHttpClientFactory();
container.Register<IHttpClientFactory>(factory);

2

যেহেতু সমস্ত সম্পাদন করার জন্য পদ্ধতিটি HttpClientব্যবহার করুন , আপনি পদ্ধতিটি এবং উপহাস করতে পারেনSendAsyncHTTP Requestsoverride SendAsyncHttpClient

যে একটি মোড়ক তৈরি HttpClientকরতে interface, নীচের মত কিছু

public interface IServiceHelper
{
    HttpClient GetClient();
}

তারপরে interfaceআপনার পরিষেবাতে নির্ভরতা ইনজেকশনের জন্য উপরে ব্যবহার করুন, নীচের নমুনা

public class SampleService
{
    private readonly IServiceHelper serviceHelper;

    public SampleService(IServiceHelper serviceHelper)
    {
        this.serviceHelper = serviceHelper;
    }

    public async Task<HttpResponseMessage> Get(int dummyParam)
    {
        try
        {
            var dummyUrl = "http://www.dummyurl.com/api/controller/" + dummyParam;
            var client = serviceHelper.GetClient();
            HttpResponseMessage response = await client.GetAsync(dummyUrl);               

            return response;
        }
        catch (Exception)
        {
            // log.
            throw;
        }
    }
}

এখন ইউনিট পরীক্ষা প্রকল্পে উপহাসের জন্য একটি সহায়ক শ্রেণি তৈরি করুন SendAsync। এখানে এটি এমন একটি FakeHttpResponseHandlerশ্রেণি যা পদ্ধতিকে inheriting DelegatingHandlerওভাররাইড করার জন্য একটি বিকল্প সরবরাহ করবে SendAsync। অগ্রাহ্য করার পর SendAsyncপ্রতিটি একটি প্রতিক্রিয়া সেটআপ পদ্ধতি প্রয়োজন HTTP Requestযা কল করছে SendAsyncপদ্ধতি, যে একটি তৈরি জন্য Dictionaryসঙ্গে keyযেমন Uriএবং valueহিসাবে HttpResponseMessageযাতে যখনই একটা হয় HTTP Requestআর যদি Uriম্যাচ SendAsyncফিরে আসবে কনফিগার HttpResponseMessage

public class FakeHttpResponseHandler : DelegatingHandler
{
    private readonly IDictionary<Uri, HttpResponseMessage> fakeServiceResponse;
    private readonly JavaScriptSerializer javaScriptSerializer;
    public FakeHttpResponseHandler()
    {
        fakeServiceResponse =  new Dictionary<Uri, HttpResponseMessage>();
        javaScriptSerializer =  new JavaScriptSerializer();
    }

    /// <summary>
    /// Used for adding fake httpResponseMessage for the httpClient operation.
    /// </summary>
    /// <typeparam name="TQueryStringParameter"> query string parameter </typeparam>
    /// <param name="uri">Service end point URL.</param>
    /// <param name="httpResponseMessage"> Response expected when the service called.</param>
    public void AddFakeServiceResponse(Uri uri, HttpResponseMessage httpResponseMessage)
    {
        fakeServiceResponse.Remove(uri);
        fakeServiceResponse.Add(uri, httpResponseMessage);
    }

    /// <summary>
    /// Used for adding fake httpResponseMessage for the httpClient operation having query string parameter.
    /// </summary>
    /// <typeparam name="TQueryStringParameter"> query string parameter </typeparam>
    /// <param name="uri">Service end point URL.</param>
    /// <param name="httpResponseMessage"> Response expected when the service called.</param>
    /// <param name="requestParameter">Query string parameter.</param>
    public void AddFakeServiceResponse<TQueryStringParameter>(Uri uri, HttpResponseMessage httpResponseMessage, TQueryStringParameter requestParameter)
    {
        var serilizedQueryStringParameter = javaScriptSerializer.Serialize(requestParameter);
        var actualUri = new Uri(string.Concat(uri, serilizedQueryStringParameter));
        fakeServiceResponse.Remove(actualUri);
        fakeServiceResponse.Add(actualUri, httpResponseMessage);
    }

    // all method in HttpClient call use SendAsync method internally so we are overriding that method here.
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if(fakeServiceResponse.ContainsKey(request.RequestUri))
        {
            return Task.FromResult(fakeServiceResponse[request.RequestUri]);
        }

        return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            RequestMessage = request,
            Content = new StringContent("Not matching fake found")
        });
    }
}

IServiceHelperঠাট্টা ফ্রেম ফ্রেম বা নীচের মতো করে এর জন্য একটি নতুন বাস্তবায়ন তৈরি করুন । এই FakeServiceHelperক্লাসটি আমরা ক্লাসটি ইনজেকশন করতে ব্যবহার করতে পারি FakeHttpResponseHandlerযাতে এটি যখনই HttpClientতৈরি করে তবে classএটি FakeHttpResponseHandler classপ্রকৃত বাস্তবায়নের পরিবর্তে ব্যবহার করবে ।

public class FakeServiceHelper : IServiceHelper
{
    private readonly DelegatingHandler delegatingHandler;

    public FakeServiceHelper(DelegatingHandler delegatingHandler)
    {
        this.delegatingHandler = delegatingHandler;
    }

    public HttpClient GetClient()
    {
        return new HttpClient(delegatingHandler);
    }
}

এবং পরীক্ষার কনফিগারটিতে FakeHttpResponseHandler classযোগ করে Uriএবং প্রত্যাশিত HttpResponseMessageUriপ্রকৃত হওয়া উচিত serviceশেষবিন্দু Uriযাতে যখন overridden SendAsyncপদ্ধতি প্রকৃত থেকে বলা হয় serviceবাস্তবায়ন মিলিয়ে নেবে Uriমধ্যে Dictionaryএবং কনফিগার সঙ্গে সাড়া HttpResponseMessageFakeHttpResponseHandler objectভুয়া IServiceHelperবাস্তবায়নে ইনজেক্টটি কনফিগার করার পরে । তারপরে FakeServiceHelper classপ্রকৃত পরিষেবাটিতে ইনজেক্ট করুন যা override SendAsyncপদ্ধতিটি ব্যবহারে প্রকৃত পরিষেবাটি তৈরি করবে ।

[TestClass]
public class SampleServiceTest
{
    private FakeHttpResponseHandler fakeHttpResponseHandler;

    [TestInitialize]
    public void Initialize()
    {
        fakeHttpResponseHandler = new FakeHttpResponseHandler();
    }

    [TestMethod]
    public async Task GetMethodShouldReturnFakeResponse()
    {
        Uri uri = new Uri("http://www.dummyurl.com/api/controller/");
        const int dummyParam = 123456;
        const string expectdBody = "Expected Response";

        var expectedHttpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(expectdBody)
        };

        fakeHttpResponseHandler.AddFakeServiceResponse(uri, expectedHttpResponseMessage, dummyParam);

        var fakeServiceHelper = new FakeServiceHelper(fakeHttpResponseHandler);

        var sut = new SampleService(fakeServiceHelper);

        var response = await sut.Get(dummyParam);

        var responseBody = await response.Content.ReadAsStringAsync();

        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        Assert.AreEqual(expectdBody, responseBody);
    }
}

গিটহাব লিঙ্ক: যা নমুনা বাস্তবায়ন করছে


এই কোডটি কীভাবে এবং কেন এই সমস্যার সমাধান করে তার ব্যাখ্যা সহ প্রশ্নটি সমাধান করতে পারে যদিও আপনার পোস্টের মান উন্নত করতে সত্যিই সহায়তা করবে এবং সম্ভবত আরও বেশি ভোটের ফলাফল হবে। মনে রাখবেন যে আপনি ভবিষ্যতে পাঠকদের জন্য প্রশ্নের উত্তর দিচ্ছেন, কেবল এখন যে ব্যক্তি জিজ্ঞাসা করছেন তা নয়। দয়া করে সম্পাদনা ব্যাখ্যা যোগ করতে পারেন এবং সীমাবদ্ধতা এবং অনুমানের কি প্রয়োগ একটি ইঙ্গিত দিতে আপনার উত্তর।
Опир Опир

প্রতিক্রিয়া আপডেটের ব্যাখ্যার জন্য আপনাকে @ Thank ধন্যবাদ।
ঘোষ-আরুন

1

আপনি রিচার্ডস্লেয়া মকএইচটিপি লাইব্রেরিটি ব্যবহার করতে পারেন যা এইচটিটিপিমেসেজহ্যান্ডলারকে বিদ্রূপ করে এবং পরীক্ষার সময় ব্যবহার করার জন্য এইচটিটিপিপ্লাইয়েন্ট অবজেক্টটি ফিরিয়ে দিতে পারে।

গিটহাব মকএইচটিটিপি

প্রধানমন্ত্রী> ইনস্টল-প্যাকেজ রিচার্ডস্লেয়া.মকএইচটিটিপি

গিটহাব ডকুমেন্টেশন থেকে

মকএইচটিপি একটি প্রতিস্থাপন এইচটিটিপিএমেসেজ হ্যান্ডলার সংজ্ঞায়িত করে, এই ইঞ্জিনটি এইচটিটিপিপ্লিনেন্টকে চালিত করে, এটি একটি সাবলীল কনফিগারেশন এপিআই সরবরাহ করে এবং একটি ক্যানড প্রতিক্রিয়া সরবরাহ করে। কলকারী (যেমন আপনার অ্যাপ্লিকেশনটির পরিষেবা স্তর) এর উপস্থিতি সম্পর্কে অজানা রয়েছেন।

গিটহাব থেকে উদাহরণ

 var mockHttp = new MockHttpMessageHandler();

// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
        .Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON

// Inject the handler or client into your application code
var client = mockHttp.ToHttpClient();

var response = await client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync("http://localhost/api/user/1234").Result;

var json = await response.Content.ReadAsStringAsync();

// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}

1

এটি একটি পুরানো প্রশ্ন, তবে আমি এখানে দেখিনি এমন একটি সমাধান দিয়ে উত্তরগুলি প্রসারিত করার তাগিদ অনুভব করছি।
আপনি মাইক্রোসফ্ট জড়ো করতে পারেন (System.Net.Http) এবং তারপরে পরীক্ষার সময় শিনসকন্টেক্সট ব্যবহার করতে পারেন।

  1. ভিএস 2017-তে, সিস্টেম. নেট.এইচটিপি সমাবেশে ডান ক্লিক করুন এবং "নকল সমাবেশ যুক্ত করুন" নির্বাচন করুন
  2. আপনার কোডটি ইউনিট পরীক্ষা পদ্ধতিতে একটি শিমসকন্টেক্সটেক্স্রেট () ব্যবহার করে রাখুন। আপনি এইচটিটিপিপ্লিনেন্টকে জাল করার পরিকল্পনা করছেন এমন কোডটি আপনি এইভাবে আলাদা করতে পারেন।
  3. আপনার বাস্তবায়ন এবং পরীক্ষার উপর নির্ভর করে, আমি আপনাকে সমস্ত পছন্দসই অভিনয় বাস্তবায়নের পরামর্শ দিচ্ছি যেখানে আপনি এইচটিটিপি ক্লায়েন্টে কোনও পদ্ধতি কল করেন এবং প্রত্যাবর্তিত মানটি নকল করতে চান। ShimHttpClient.AllInferences ব্যবহার করা আপনার পরীক্ষার সময় তৈরি সমস্ত দৃষ্টিতে আপনার প্রয়োগকে নকল করবে। উদাহরণস্বরূপ, আপনি যদি getAsync () পদ্ধতিটি নকল করতে চান তবে নিম্নলিখিতগুলি করুন:

    [TestMethod]
    public void FakeHttpClient()
    {
        using (ShimsContext.Create())
        {
            System.Net.Http.Fakes.ShimHttpClient.AllInstances.GetAsyncString = (c, requestUri) =>
            {
              //Return a service unavailable response
              var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
              var task = Task.FromResult(httpResponseMessage);
              return task;
            };
    
            //your implementation will use the fake method(s) automatically
            var client = new Connection(_httpClient);
            client.doSomething(); 
        }
    }

1

আমি খুব সাধারণ কিছু করেছি, যেমন আমি একটি ডিআই পরিবেশে ছিলাম।

public class HttpHelper : IHttpHelper
{
    private ILogHelper _logHelper;

    public HttpHelper(ILogHelper logHelper)
    {
        _logHelper = logHelper;
    }

    public virtual async Task<HttpResponseMessage> GetAsync(string uri, Dictionary<string, string> headers = null)
    {
        HttpResponseMessage response;
        using (var client = new HttpClient())
        {
            if (headers != null)
            {
                foreach (var h in headers)
                {
                    client.DefaultRequestHeaders.Add(h.Key, h.Value);
                }
            }
            response = await client.GetAsync(uri);
        }

        return response;
    }

    public async Task<T> GetAsync<T>(string uri, Dictionary<string, string> headers = null)
    {
        ...

        rawResponse = await GetAsync(uri, headers);

        ...
    }

}

উপহাসটি হ'ল:

    [TestInitialize]
    public void Initialize()
    {
       ...
        _httpHelper = new Mock<HttpHelper>(_logHelper.Object) { CallBase = true };
       ...
    }

    [TestMethod]
    public async Task SuccessStatusCode_WithAuthHeader()
    {
        ...

        _httpHelper.Setup(m => m.GetAsync(_uri, myHeaders)).Returns(
            Task<HttpResponseMessage>.Factory.StartNew(() =>
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                {
                    Content = new StringContent(JsonConvert.SerializeObject(_testData))
                };
            })
        );
        var result = await _httpHelper.Object.GetAsync<TestDTO>(...);

        Assert.AreEqual(...);
    }

1

আপনার যা দরকার তা হ'ল HttpMessageHandlerক্লাসের একটি পরীক্ষা সংস্করণ যা আপনি HttpClientctor এ পাস করেন । মূল বিষয়টি হ'ল আপনার পরীক্ষার HttpMessageHandlerশ্রেণিতে এমন একটি HttpRequestHandlerপ্রতিনিধি থাকবে যা কলকারীরা তাদের পছন্দ HttpRequestমতো উপায় সেট এবং পরিচালনা করতে পারে।

public class FakeHttpMessageHandler : HttpMessageHandler
    {
        public Func<HttpRequestMessage, CancellationToken, HttpResponseMessage> HttpRequestHandler { get; set; } =
        (r, c) => 
            new HttpResponseMessage
            {
                ReasonPhrase = r.RequestUri.AbsoluteUri,
                StatusCode = HttpStatusCode.OK
            };


        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return Task.FromResult(HttpRequestHandler(request, cancellationToken));
        }
    }

আপনি এই শ্রেণীর উদাহরণ ব্যবহার করতে পারেন একটি কংক্রিট এইচটিটিপি ক্লিনেন্ট উদাহরণ তৈরি করতে। এইচটিটিপিআরকিউস্টেস্ট হ্যান্ডলার প্রতিনিধিটির মাধ্যমে আপনি এইচটিটিপিপ্লিনেন্টের বহির্গামী HTTP অনুরোধগুলির উপর সম্পূর্ণ নিয়ন্ত্রণ রাখবেন।


1

দ্বারা অনুপ্রাণিত PointZeroTwo এর উত্তর , এখানে ব্যবহার করে একটি নমুনা NUnit এবং FakeItEasy

SystemUnderTest এই উদাহরণে আপনি যে শ্রেণিটি পরীক্ষা করতে চান তা হল - এর জন্য কোনও নমুনা সামগ্রী দেওয়া হয়নি তবে আমি ধরে নিই যে আপনার কাছে এটি ইতিমধ্যে রয়েছে!

[TestFixture]
public class HttpClientTests
{
    private ISystemUnderTest _systemUnderTest;
    private HttpMessageHandler _mockMessageHandler;

    [SetUp]
    public void Setup()
    {
        _mockMessageHandler = A.Fake<HttpMessageHandler>();
        var httpClient = new HttpClient(_mockMessageHandler);

        _systemUnderTest = new SystemUnderTest(httpClient);
    }

    [Test]
    public void HttpError()
    {
        // Arrange
        A.CallTo(_mockMessageHandler)
            .Where(x => x.Method.Name == "SendAsync")
            .WithReturnType<Task<HttpResponseMessage>>()
            .Returns(Task.FromResult(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.InternalServerError,
                Content = new StringContent("abcd")
            }));

        // Act
        var result = _systemUnderTest.DoSomething();

        // Assert
        // Assert.AreEqual(...);
    }
}

আমি যদি "x.Method.Name" এর বিপরীতে উল্লিখিত পদ্ধতিতে একটি পরামিতি পাস করতে চাই তবে কী ..?
শৈলেশ

0

আপনার বর্তমান প্রকল্পে পরিবর্তনের জন্য কিছু কোড থাকতে পারে তবে নতুন প্রকল্পগুলির জন্য আপনার অবশ্যই ফ্লুরল ব্যবহার করা উচিত।

https://flurl.dev

এটি সাবলীল ইন্টারফেস সহ .NET এর জন্য একটি এইচটিটিপি ক্লায়েন্ট লাইব্রেরি যা এইচটিটিপি অনুরোধগুলি করতে এটি ব্যবহার করে এমন কোডের জন্য বিশেষভাবে টেস্টিবিলিটি সক্ষম করে।

ওয়েবসাইটে প্রচুর কোড নমুনা রয়েছে তবে সংক্ষেপে আপনি এটি আপনার কোডটিতে এটি ব্যবহার করেন।

Usings যোগ করুন।

using Flurl;
using Flurl.Http;

একটি অনুরোধ পাঠান এবং প্রতিক্রিয়া পড়ুন।

public async Task SendGetRequest()
{
   var response = await "https://example.com".GetAsync();
   // ...
}

ইউনিট পরীক্ষায় ফ্লুরল একটি মক হিসাবে কাজ করে যা পছন্দসই আচরণ করতে এবং যে কলগুলি হয়েছিল তা যাচাই করতে কনফিগার করা যায়।

using (var httpTest = new HttpTest())
{
   // Arrange
   httpTest.RespondWith("OK", 200);

   // Act
   await sut.SendGetRequest();

   // Assert
   httpTest.ShouldHaveCalled("https://example.com")
      .WithVerb(HttpMethod.Get);
}

0

সাবধানতার সাথে অনুসন্ধানের পরে, আমি এটি সম্পাদন করার জন্য সর্বোত্তম পদ্ধতির সন্ধান করেছি।

    private HttpResponseMessage response;

    [SetUp]
    public void Setup()
    {
        var handlerMock = new Mock<HttpMessageHandler>();

        handlerMock
           .Protected()
           .Setup<Task<HttpResponseMessage>>(
              "SendAsync",
              ItExpr.IsAny<HttpRequestMessage>(),
              ItExpr.IsAny<CancellationToken>())
           // This line will let you to change the response in each test method
           .ReturnsAsync(() => response);

        _httpClient = new HttpClient(handlerMock.Object);

        yourClinet = new YourClient( _httpClient);
    }

আপনি লক্ষ্য করেছেন যে আমি মো এবং মো। প্রোটেক্টেড প্যাকেজগুলি ব্যবহার করেছি।


0

আমার 2 সেন্ট যোগ করতে। নির্দিষ্ট http অনুরোধের পদ্ধতিগুলি উপহাস করতে হয় হয় পোস্ট করুন। এটি আমার পক্ষে কাজ করেছে।

mockHttpMessageHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(a => a.Method == HttpMethod.Get), ItExpr.IsAny<CancellationToken>())
                                                .Returns(Task.FromResult(new HttpResponseMessage()
                                                {
                                                    StatusCode = HttpStatusCode.OK,
                                                    Content = new StringContent(""),
                                                })).Verifiable();
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.