Saya memiliki beberapa masalah saat mencoba membungkus kode saya untuk digunakan dalam pengujian unit. Masalahnya adalah ini. Saya Memiliki antarmuka IHttpHandler:
public interface IHttpHandler
{
HttpClient client { get; }
}
Dan kelas yang menggunakannya, HttpHandler:
public class HttpHandler : IHttpHandler
{
public HttpClient client
{
get
{
return new HttpClient();
}
}
}
Dan kemudian kelas Connection, yang menggunakan simpleIOC untuk memasukkan implementasi klien:
public class Connection
{
private IHttpHandler _httpClient;
public Connection(IHttpHandler httpClient)
{
_httpClient = httpClient;
}
}
Dan kemudian saya memiliki proyek pengujian unit yang memiliki kelas ini:
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?
}
Sekarang jelas saya akan memiliki metode di kelas Connection yang akan mengambil data (JSON) dari back end saya. Namun, saya ingin menulis tes unit untuk kelas ini, dan jelas saya tidak ingin menulis tes terhadap back end yang sebenarnya, melainkan yang diejek. Saya telah mencoba google jawaban yang bagus untuk ini tanpa hasil yang besar. Saya dapat dan telah menggunakan Moq untuk mengejek sebelumnya, tetapi tidak pernah pada sesuatu seperti httpClient. Bagaimana saya harus mendekati masalah ini?
Terima kasih sebelumnya.
sumber
HttpClient
di antarmuka Anda adalah tempat masalahnya. Anda memaksa klien Anda untuk menggunakanHttpClient
kelas beton. Sebaliknya, Anda harus mengekspos abstraksi fileHttpClient
.Jawaban:
Antarmuka Anda mengekspos
HttpClient
kelas konkret , oleh karena itu setiap kelas yang menggunakan antarmuka ini terikat padanya, ini berarti kelas tersebut tidak dapat diejek.HttpClient
tidak mewarisi dari antarmuka mana pun, jadi Anda harus menulisnya sendiri. Saya menyarankan pola seperti dekorator :Dan kelas Anda akan terlihat seperti ini:
Inti dari semua ini adalah
HttpClientHandler
membuat sendiriHttpClient
, tentu saja Anda dapat membuat beberapa kelas yang diimplementasikanIHttpHandler
dengan cara yang berbeda.Masalah utama dengan pendekatan ini adalah bahwa Anda secara efektif menulis kelas yang hanya memanggil metode di kelas lain, namun Anda dapat membuat kelas yang mewarisi dari
HttpClient
(Lihat contoh Nkosi , ini adalah pendekatan yang jauh lebih baik daripada milik saya). Hidup akan jauh lebih mudah jikaHttpClient
memiliki antarmuka yang dapat Anda tiru, sayangnya tidak.Namun, contoh ini bukanlah tiket emas.
IHttpHandler
masih mengandalkanHttpResponseMessage
, yang termasuk dalamSystem.Net.Http
namespace, oleh karena itu jika Anda memerlukan implementasi lain selainHttpClient
, Anda harus melakukan beberapa jenis pemetaan untuk mengubah tanggapan mereka menjadiHttpResponseMessage
objek. Hal ini tentu saja hanya masalah jika Anda perlu menggunakan beberapa implementasi dariIHttpHandler
tetapi tidak terlihat seperti Anda melakukannya itu bukan akhir dari dunia, tapi sesuatu untuk dipikirkan.Bagaimanapun, Anda dapat dengan mudah mengejek
IHttpHandler
tanpa harus khawatir tentangHttpClient
kelas konkret karena telah disarikan.Saya merekomendasikan pengujian metode non-asinkron , karena ini masih memanggil metode asinkron tetapi tanpa kerumitan harus khawatir tentang pengujian unit metode asinkron, lihat di sini
sumber
Ekstensibilitas HttpClient terletak pada
HttpMessageHandler
diteruskan ke konstruktor. Maksudnya adalah untuk mengizinkan implementasi khusus platform, tetapi Anda juga dapat memalsukannya. Tidak perlu membuat pembungkus dekorator untuk HttpClient.Jika Anda lebih suka DSL daripada menggunakan Moq, saya memiliki perpustakaan di GitHub / Nuget yang membuat segalanya menjadi lebih mudah: https://github.com/richardszalay/mockhttp
sumber
var client = new HttpClient()
denganvar client = ClientFactory()
dan siapkan bidanginternal static Func<HttpClient> ClientFactory = () => new HttpClient();
dan pada tingkat pengujian Anda dapat menulis ulang bidang ini.HttpClientFactory
seperti dalamFunc<HttpClient>
. Mengingat saya melihat HttpClient murni sebagai detail implementasi dan bukan ketergantungan, saya akan menggunakan statika seperti yang saya ilustrasikan di atas. Saya sepenuhnya baik-baik saja dengan tes yang memanipulasi internal. Jika saya peduli dengan pure-ism, saya akan berdiri di server penuh dan menguji jalur kode langsung. Menggunakan tiruan apa pun berarti Anda menerima perkiraan perilaku, bukan perilaku sebenarnya.Saya setuju dengan beberapa jawaban lain bahwa pendekatan terbaik adalah mengejek HttpMessageHandler daripada membungkus HttpClient. Jawaban ini unik karena masih menyuntikkan HttpClient, memungkinkannya menjadi tunggal atau dikelola dengan injeksi ketergantungan.
"HttpClient dimaksudkan untuk dipakai sekali dan digunakan kembali sepanjang masa pakai aplikasi." ( Sumber ).
Mengolok-olok HttpMessageHandler bisa sedikit rumit karena SendAsync dilindungi. Berikut contoh lengkapnya, menggunakan xunit dan Moq.
sumber
mockMessageHandler.Protected()
adalah si pembunuh. Terima kasih untuk contoh ini. Ini memungkinkan untuk menulis tes tanpa mengubah sumber sama sekali..ReturnsAsync(new HttpResponseMessage {StatusCode = HttpStatusCode.OK, Content = new StringContent(testContent)})
Ini adalah pertanyaan umum, dan saya sangat menginginkan kemampuan untuk mengejek HttpClient, tetapi saya rasa akhirnya saya menyadari bahwa Anda tidak boleh mengejek HttpClient. Tampaknya logis untuk melakukannya, tetapi saya pikir kami telah dicuci otak oleh hal-hal yang kami lihat di perpustakaan sumber terbuka.
Kami sering melihat "Klien" di luar sana yang kami tiru dalam kode kami sehingga kami dapat menguji secara terpisah, jadi kami secara otomatis mencoba menerapkan prinsip yang sama ke HttpClient. HttpClient sebenarnya melakukan banyak hal; Anda dapat menganggapnya sebagai manajer untuk HttpMessageHandler, jadi Anda tidak ingin mengejeknya, dan itulah mengapa masih belum memiliki antarmuka. Bagian yang benar-benar Anda minati untuk pengujian unit, atau bahkan mendesain layanan Anda, adalah HttpMessageHandler karena itulah yang mengembalikan respons, dan Anda dapat mengejeknya.
Juga perlu diperhatikan bahwa Anda mungkin harus mulai memperlakukan HttpClient seperti kesepakatan yang lebih besar. Misalnya: Jaga instatiating Anda dari HttpClients baru seminimal mungkin. Gunakan kembali, mereka dirancang untuk digunakan kembali dan menggunakan sumber daya yang lebih sedikit jika Anda melakukannya. Jika Anda mulai memperlakukannya seperti kesepakatan yang lebih besar, akan terasa lebih salah jika ingin mengejeknya dan sekarang penangan pesan akan mulai menjadi hal yang Anda masukkan, bukan klien.
Dengan kata lain, rancang dependensi Anda di sekitar handler, bukan di klien. Bahkan lebih baik, "layanan" abstrak yang menggunakan HttpClient yang memungkinkan Anda memasukkan penangan, dan menggunakannya sebagai dependensi yang dapat diinjeksi. Kemudian dalam pengujian Anda, Anda dapat memalsukan penangan untuk mengontrol respons untuk menyiapkan pengujian Anda.
Membungkus HttpClient adalah pemborosan waktu yang gila-gilaan.
Pembaruan: Lihat contoh Joshua Dooms. Itu persis seperti yang saya rekomendasikan.
sumber
Seperti juga disebutkan dalam komentar Anda perlu abstrak pergi
HttpClient
agar tidak digabungkan untuk itu. Saya telah melakukan hal serupa di masa lalu. Saya akan mencoba menyesuaikan apa yang saya lakukan dengan apa yang Anda coba lakukan.Pertama, lihat
HttpClient
kelas dan putuskan fungsionalitas apa yang disediakan yang akan dibutuhkan.Berikut ini kemungkinannya:
Sekali lagi, seperti yang dinyatakan sebelumnya, ini untuk tujuan tertentu. Saya benar-benar mengabstraksi sebagian besar ketergantungan pada apa pun yang berhubungan dengan
HttpClient
dan berfokus pada apa yang saya inginkan kembali. Anda harus mengevaluasi bagaimana Anda ingin mengabstraksiHttpClient
untuk menyediakan hanya fungsionalitas yang diperlukan yang Anda inginkan.Ini sekarang akan memungkinkan Anda untuk hanya mengejek apa yang perlu diuji.
Saya bahkan akan merekomendasikan untuk menghapus
IHttpHandler
sepenuhnya dan menggunakanHttpClient
abstraksiIHttpClient
. Tetapi saya tidak memilih karena Anda dapat mengganti badan antarmuka penangan Anda dengan anggota klien yang diabstraksi.Implementasi dari
IHttpClient
kemudian dapat digunakan untuk membungkus / mengadaptasi nyata / konkretHttpClient
atau objek lain dalam hal ini, yang dapat digunakan untuk membuat permintaan HTTP karena apa yang Anda inginkan adalah layanan yang menyediakan fungsionalitas itu sebagaimana yang ditentukanHttpClient
secara khusus. Menggunakan abstraksi adalah pendekatan yang bersih (Pendapat saya) dan SOLID dan dapat membuat kode Anda lebih mudah dipelihara jika Anda perlu mengganti klien yang mendasarinya untuk sesuatu yang lain saat kerangka kerja berubah.Berikut ini cuplikan bagaimana penerapan dapat dilakukan.
Seperti yang Anda lihat pada contoh di atas, banyak pekerjaan berat yang biasanya terkait dengan penggunaan
HttpClient
tersembunyi di balik abstraksi.Kelas koneksi Anda kemudian dapat diinjeksi dengan klien yang diabstraksi
Tes Anda kemudian dapat mengejek apa yang dibutuhkan untuk SUT Anda
sumber
HttpClient
dan adaptor untuk membuat instance spesifik Anda menggunakanHttpClientFactory
. Melakukan hal ini membuat pengujian logika di luar permintaan HTTP menjadi sepele, yang merupakan tujuannya di sini.Berdasarkan jawaban lain, saya menyarankan kode ini, yang tidak memiliki ketergantungan di luar:
sumber
HttpMessageHandler
sendiri membuat itu hampir tidak mungkin - dan Anda harus melakukannya karena metodenyaprotected internal
.Saya pikir masalahnya adalah Anda mendapatkannya sedikit terbalik.
Jika Anda melihat kelas di atas, saya rasa inilah yang Anda inginkan. Microsoft menganjurkan agar klien tetap hidup untuk kinerja yang optimal, sehingga jenis struktur ini memungkinkan Anda melakukannya. Juga HttpMessageHandler adalah kelas abstrak dan oleh karena itu dapat diolok-olok. Metode pengujian Anda akan terlihat seperti ini:
Ini memungkinkan Anda untuk menguji logika Anda sambil mengejek perilaku HttpClient.
Maaf teman-teman, setelah menulis ini dan mencobanya sendiri, saya menyadari bahwa Anda tidak dapat mengejek metode yang dilindungi di HttpMessageHandler. Saya kemudian menambahkan kode berikut untuk memungkinkan injeksi tiruan yang tepat.
Tes yang ditulis dengan ini kemudian terlihat seperti berikut:
sumber
Salah satu kolega saya memperhatikan bahwa sebagian besar
HttpClient
metode semuanya memanggil diSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
bawah tenda, yang merupakan metode virtual dariHttpMessageInvoker
:Sejauh ini, cara termudah untuk mengejek
HttpClient
adalah dengan mengejek metode tertentu itu:dan kode Anda bisa memanggil sebagian besar (tapi tidak semua)
HttpClient
metode kelas, termasuk regularPeriksa di sini untuk mengonfirmasi https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs
sumber
SendAsync(HttpRequestMessage)
secara langsung. Jika Anda dapat mengubah kode untuk tidak menggunakan fungsi kemudahan ini, maka mengejek HttpClient secara langsung dengan menimpaSendAsync
sebenarnya adalah solusi terbersih yang pernah saya temukan.Salah satu alternatifnya adalah menyiapkan server HTTP rintisan yang mengembalikan respons terekam berdasarkan pola yang cocok dengan url permintaan, yang berarti Anda menguji permintaan HTTP nyata, bukan mengejek. Secara historis, ini akan membutuhkan upaya pengembangan yang signifikan dan akan sangat lambat untuk dipertimbangkan untuk pengujian unit, namun pustaka OSS WireMock.net mudah digunakan dan cukup cepat untuk dijalankan dengan banyak pengujian sehingga mungkin perlu dipertimbangkan. Penyiapannya adalah beberapa baris kode:
Anda dapat menemukan detail dan panduan lebih lanjut tentang penggunaan wiremock dalam pengujian di sini.
sumber
Inilah solusi sederhana, yang bekerja dengan baik untuk saya.
Menggunakan pustaka moq mocking.
Sumber: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
sumber
SendAsync
jadi tidak diperlukan pengaturan tambahan.Saya tidak yakin dengan banyak jawaban.
Pertama-tama, bayangkan Anda ingin menguji unit metode yang digunakan
HttpClient
. Anda tidak boleh membuat instanceHttpClient
langsung dalam implementasi Anda. Anda harus menyuntikkan pabrik dengan tanggung jawab menyediakan contohHttpClient
untuk Anda. Dengan cara itu Anda dapat mengejek nanti di pabrik itu dan mengembalikan mana pun yangHttpClient
Anda inginkan (misalnya: tiruanHttpClient
dan bukan yang asli).Jadi, Anda akan memiliki pabrik seperti berikut:
Dan implementasinya:
Tentu saja Anda perlu mendaftar di Kontainer IoC Anda untuk implementasi ini. Jika Anda menggunakan Autofac, itu akan menjadi seperti ini:
Sekarang Anda akan memiliki implementasi yang tepat dan dapat diuji. Bayangkan metode Anda seperti ini:
Sekarang bagian pengujian.
HttpClient
meluasHttpMessageHandler
, yang abstrak. Mari kita buat "tiruan"HttpMessageHandler
yang menerima delegasi sehingga saat kita menggunakan tiruan kita juga bisa mengatur setiap perilaku untuk setiap pengujian.Dan sekarang, dan dengan bantuan Moq (dan FluentAssertions, pustaka yang membuat pengujian unit lebih mudah dibaca), kami memiliki semua yang diperlukan untuk menguji unit metode kami PostAsync yang menggunakan
HttpClient
Jelas tes ini konyol, dan kami benar-benar menguji tiruan kami. Tapi Anda mengerti. Anda harus menguji logika yang bermakna tergantung pada penerapan Anda seperti ..
Tujuan dari jawaban ini adalah untuk menguji sesuatu yang menggunakan HttpClient dan ini adalah cara bersih yang bagus untuk melakukannya.
sumber
Bergabung dengan pesta agak terlambat, tetapi saya suka menggunakan wiremocking ( https://github.com/WireMock-Net/WireMock.Net ) bila memungkinkan dalam uji integrasi layanan mikro inti dotnet dengan dependensi REST hilir.
Dengan mengimplementasikan TestHttpClientFactory yang memperluas IHttpClientFactory kita bisa mengganti metode
HttpClient CreateClient (nama string)
Jadi, saat menggunakan klien bernama dalam aplikasi Anda, Anda memegang kendali untuk mengembalikan HttpClient yang terhubung ke wiremock Anda.
Hal yang baik tentang pendekatan ini adalah Anda tidak mengubah apa pun dalam aplikasi yang Anda uji, dan memungkinkan pengujian integrasi kursus melakukan permintaan REST aktual ke layanan Anda dan mengejek json (atau apa pun) yang harus dikembalikan oleh permintaan downstream aktual. Hal ini mengarah pada pengujian yang ringkas dan sesedikit mungkin ejekan dalam aplikasi Anda.
dan
sumber
Karena
HttpClient
menggunakanSendAsync
metode untuk melakukan semuaHTTP Requests
, Anda dapatoverride SendAsync
menggunakan metode dan mengejek fileHttpClient
.Untuk bungkus itu membuat
HttpClient
menjadiinterface
, seperti di bawah iniKemudian gunakan di atas
interface
untuk injeksi dependensi dalam layanan Anda, contoh di bawahSekarang dalam proyek uji unit buat kelas pembantu untuk mengejek
SendAsync
. Ini diaFakeHttpResponseHandler
kelasinheriting
DelegatingHandler
yang akan memberikan opsi untuk menggantiSendAsync
metode. Setelah menggantiSendAsync
metode perlu menyiapkan respons untuk masing-masingHTTP Request
yang memanggilSendAsync
metode, untuk itu buatDictionary
dengankey
asUri
danvalue
asHttpResponseMessage
sehingga setiap kali adaHTTP Request
dan jikaUri
pertandinganSendAsync
akan mengembalikan konfigurasiHttpResponseMessage
.Buat implementasi baru untuk
IServiceHelper
dengan kerangka kerja tiruan atau seperti di bawah ini.FakeServiceHelper
Kelas ini dapat kita gunakan untuk menginjeksiFakeHttpResponseHandler
kelas sehingga setiap kaliHttpClient
dibuat oleh iniclass
akan digunakanFakeHttpResponseHandler class
sebagai pengganti implementasi yang sebenarnya.Dan di konfigurasi uji
FakeHttpResponseHandler class
dengan menambahkanUri
dan diharapkanHttpResponseMessage
. IniUri
harus menjadiservice
titik akhir yang sebenarnyaUri
sehingga ketikaoverridden SendAsync
metode dipanggil dariservice
implementasi yang sebenarnya, ia akan cocok denganUri
inDictionary
dan merespons dengan yang dikonfigurasiHttpResponseMessage
. Setelah mengonfigurasi, injeksiFakeHttpResponseHandler object
keIServiceHelper
implementasi palsu . Kemudian masukkanFakeServiceHelper class
ke layanan aktual yang akan membuat layanan aktual menggunakanoverride SendAsync
metode tersebut.GitHub Link: yang memiliki contoh implementasi
sumber
Anda bisa menggunakan pustaka RichardSzalay MockHttp yang mengolok-olok HttpMessageHandler dan dapat mengembalikan objek HttpClient untuk digunakan selama pengujian.
GitHub MockHttp
PM> Instal-Paket RichardSzalay.MockHttp
Dari dokumentasi GitHub
Contoh dari GitHub
sumber
Ini adalah pertanyaan lama, tetapi saya merasakan dorongan untuk memberikan jawaban dengan solusi yang tidak saya lihat di sini.
Anda dapat memalsukan Microsoft assemly (System.Net.Http) dan kemudian menggunakan ShinsContext selama pengujian.
Bergantung pada implementasi dan pengujian Anda, saya akan menyarankan untuk menerapkan semua tindakan yang diinginkan di mana Anda memanggil metode pada HttpClient dan ingin memalsukan nilai yang dikembalikan. Menggunakan ShimHttpClient.AllInstances akan memalsukan implementasi Anda di semua instance yang dibuat selama pengujian. Misalnya, jika Anda ingin memalsukan metode GetAsync (), lakukan hal berikut:
sumber
Saya melakukan sesuatu yang sangat sederhana, karena saya berada di lingkungan DI.
dan tiruannya adalah:
sumber
Yang Anda butuhkan hanyalah versi uji
HttpMessageHandler
kelas yang Anda teruskan keHttpClient
ctor. Poin utamanya adalah bahwaHttpMessageHandler
kelas pengujian Anda akan memilikiHttpRequestHandler
delegasi yang dapat diatur oleh pemanggil dan hanya menanganiHttpRequest
seperti yang mereka inginkan.Anda dapat menggunakan instance kelas ini untuk membuat instance HttpClient yang konkret. Melalui delegasi HttpRequestHandler Anda memiliki kendali penuh atas permintaan http keluar dari HttpClient.
sumber
Terinspirasi oleh jawaban PointZeroTwo , berikut ini contoh menggunakan NUnit dan FakeItEasy .
SystemUnderTest
dalam contoh ini adalah kelas yang ingin Anda uji - tidak ada konten sampel yang diberikan untuk itu, tetapi saya berasumsi bahwa Anda sudah memilikinya!sumber
Mungkin akan ada beberapa kode untuk diubah dalam proyek Anda saat ini, tetapi untuk proyek baru Anda harus benar-benar mempertimbangkan untuk menggunakan Flurl.
https://flurl.dev
Ini adalah pustaka klien HTTP untuk .NET dengan antarmuka fasih yang secara khusus memungkinkan pengujian untuk kode yang menggunakannya untuk membuat permintaan HTTP.
Ada banyak contoh kode di situs web tetapi singkatnya Anda menggunakannya seperti ini di kode Anda.
Tambahkan penggunaan.
Kirim permintaan get dan baca tanggapannya.
Dalam pengujian unit Flurl bertindak sebagai tiruan yang dapat dikonfigurasi untuk berperilaku seperti yang diinginkan dan juga untuk memverifikasi panggilan yang dilakukan.
sumber
Setelah mencari dengan cermat, saya menemukan pendekatan terbaik untuk mencapai ini.
sumber
Untuk menambahkan 2 sen saya. Untuk meniru metode permintaan http tertentu, baik Dapatkan atau Posting. Ini berhasil untuk saya.
sumber