Saya memiliki beberapa unit test yang mengharapkan 'waktu saat ini' berbeda dari DateTime. Sekarang dan saya tidak ingin mengubah waktu komputer, jelas.
Apa strategi terbaik untuk mencapai ini?
c#
unit-testing
datetime
mstest
systemtime
Pedro
sumber
sumber
DateTime
. Ini akan menjadi yang Anda uji, metode yang ada hanya akan memanggil kelebihan bebannow
.Jawaban:
The terbaik strategi untuk membungkus waktu saat ini dalam abstraksi dan menyuntikkan abstraksi yang ke konsumen .
Atau , Anda juga dapat menentukan abstraksi waktu sebagai Konteks Sekitar :
Ini akan memungkinkan Anda untuk mengkonsumsinya seperti ini:
Dalam tes unit, Anda bisa mengganti
TimeProvider.Current
dengan objek Uji Ganda / Mock. Contoh menggunakan Moq:Namun, ketika pengujian unit dengan keadaan statis, selalu ingat untuk merobohkan perlengkapan Anda dengan menelepon
TimeProvider.ResetToDefault()
.sumber
DateTime.UtcNow
dan sejenisnya, tetapi ulasan kode adalah ide yang bagus.Ini semua adalah jawaban yang baik, inilah yang saya lakukan pada proyek yang berbeda:
Pemakaian:
Dapatkan Tanggal NYATA hari ini
Alih-alih menggunakan DateTime. Sekarang, Anda perlu menggunakan
SystemTime.Now()
... Ini bukan perubahan yang sulit tetapi solusi ini mungkin tidak ideal untuk semua proyek.Time Travelling (Ayo 5 tahun ke depan)
Dapatkan "Hari Ini" Palsu kami (akan 5 tahun dari 'hari ini')
Setel ulang tanggal
sumber
Tahi lalat:
Penafian - Saya mengerjakan Moles
sumber
Anda memiliki beberapa opsi untuk melakukannya:
Gunakan kerangka kerja mengejek dan gunakan DateTimeService (Menerapkan kelas pembungkus kecil dan menyuntikkannya ke kode produksi). Implementasi wrapper akan mengakses DateTime dan dalam tes Anda akan dapat mengejek kelas wrapper.
Gunakan Typemock Isolator , itu bisa memalsukan DateTime. Sekarang dan tidak akan mengharuskan Anda untuk mengubah kode yang sedang diuji.
Gunakan Moles , itu juga bisa memalsukan DateTime. Sekarang dan tidak akan memerlukan perubahan dalam kode produksi.
Beberapa contoh:
Kelas wrapper menggunakan Moq:
Memalsukan DateTime secara langsung menggunakan Isolator:
Penafian - Saya bekerja di Typemock
sumber
Tambahkan perakitan palsu untuk Sistem (klik kanan pada Referensi sistem => Tambahkan perakitan palsu).
Dan tulis ke dalam metode pengujian Anda:
sumber
Mengenai jawaban @crabcrusherclamcollector, ada masalah saat menggunakan pendekatan itu dalam kueri EF (System.NotSupportedException: Node ekspresi LINQ tipe 'Invoke' tidak didukung di LINQ to Entities). Saya memodifikasi implementasi menjadi:
sumber
Untuk menguji kode yang bergantung pada
System.DateTime
,system.dll
harus diejek.Ada dua kerangka kerja yang saya tahu melakukan hal ini. Microsoft palsu dan Smocks .
Microsoft palsu memerlukan visual studio 2012 ultimatum dan langsung bekerja dari compton.
Smocks adalah sumber terbuka dan sangat mudah digunakan. Itu dapat diunduh menggunakan NuGet.
Berikut ini menunjukkan tiruan dari
System.DateTime
:sumber
Utas aman
SystemClock
digunakanThreadLocal<T>
sangat bagus untuk saya.ThreadLocal<T>
tersedia di .Net Framework v4.0 dan lebih tinggi.Contoh penggunaan:
sumber
Now
instance saat ini .AsyncLocal
daripadaThreadLocal
Console.WriteLine(SystemClock.Now); SystemClock.Set(new DateTime(2017, 01, 01)); Console.WriteLine(SystemClock.Now); await Task.Delay(1000); Console.WriteLine(SystemClock.Now);
Saya mengalami masalah yang sama tetapi menemukan proyek penelitian dari Microsoft yang memecahkan masalah ini.
http://research.microsoft.com/en-us/projects/moles/
Tahi lalat adalah kerangka kerja ringan untuk rintisan pengujian dan jalan memutar di .NET yang didasarkan pada delegasi. Tahi lalat dapat digunakan untuk memutar metode .NET, termasuk metode non-virtual / statis dalam tipe tertutup
Kode sampel telah dimodifikasi dari aslinya.
Saya telah melakukan apa yang disarankan orang lain dan mengabstraksi DateTime menjadi penyedia. Rasanya salah dan saya merasa terlalu banyak hanya untuk pengujian. Saya akan menerapkan ini ke dalam proyek pribadi saya malam ini.
sumber
Satu catatan khusus tentang mengejek
DateTime.Now
dengan TypeMock ...Nilai
DateTime.Now
harus ditempatkan ke dalam variabel agar dapat diejek dengan benar. Sebagai contoh:Ini tidak bekerja:
Namun, ini tidak:
sumber
Objek Mock.
DateTime tiruan yang mengembalikan Sekarang yang sesuai untuk pengujian Anda.
sumber
Saya terkejut tidak ada yang menyarankan salah satu cara yang paling jelas:
Kemudian Anda dapat dengan mudah menimpa metode ini dalam tes ganda Anda.
Saya juga agak suka menyuntikkan
TimeProvider
kelas dalam beberapa kasus, tetapi untuk yang lain, ini lebih dari cukup. Saya mungkin lebih menyukaiTimeProvider
versi ini jika Anda perlu menggunakan kembali ini di beberapa kelas.EDIT: Bagi siapa pun yang tertarik, ini disebut menambahkan "jahitan" ke kelas Anda, titik di mana Anda dapat menghubungkan ke perilaku itu untuk memodifikasinya (untuk tujuan pengujian atau sebaliknya) tanpa benar-benar harus mengubah kode di kelas.
sumber
Praktik yang baik adalah, ketika DateTimeProvider mengimplementasikan IDisposable.
Selanjutnya, Anda dapat menyuntikkan DateTime palsu Anda untuk pengujian unit
Lihat contoh Contoh DateTimeProvider
sumber
Salah satu cara bersih untuk melakukan ini adalah dengan menyuntikkan VirtualTime. Ini memungkinkan Anda untuk mengontrol waktu. Pertama instal VirtualTime
Itu memungkinkan, misalnya, membuat waktu yang bergerak 5 kali lebih cepat pada semua panggilan ke DateTime. Sekarang atau UtcNow
Untuk membuat waktu bergerak lebih lambat, mis. 5 kali lebih lambat
Untuk membuat waktu tetap berdiri lakukan
Bergerak mundur dalam waktu belum diuji
Berikut ini adalah contoh tes:
Anda dapat memeriksa lebih banyak tes di sini:
https://github.com/VirtualTime/VirtualTime/blob/master/VirtualTimeLib.Tests/when_virtual_time_is_used.cs
Apa yang diberikan DateTime.Now.ToVirtualTime adalah instance ITime yang Anda berikan ke metode / kelas yang bergantung pada ITime. beberapa DateTime.Now.ToVirtualTime diatur dalam wadah DI pilihan Anda
Berikut adalah contoh lain menyuntikkan ke kelas contrustor
sumber
Kami menggunakan objek SystemTime statis, tetapi mengalami masalah menjalankan tes unit paralel. Saya mencoba menggunakan solusi Henk van Boeijen tetapi memiliki masalah di thread asynchronous yang muncul, akhirnya menggunakan AsyncLocal dengan cara yang mirip dengan di bawah ini:
Bersumber dari https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
sumber
Sebuah pertanyaan lama, tetapi masih valid.
Pendekatan saya adalah membuat antarmuka dan kelas baru untuk menutup
System.DateTime.Now
panggilanAntarmuka ini dapat disuntikkan ke kelas mana saja yang perlu mendapatkan tanggal dan waktu saat ini. Dalam contoh ini, saya memiliki kelas yang menambahkan rentang waktu ke tanggal dan waktu saat ini (sebuah unit yang dapat diuji System.DateTime.Now.Add (TimeSpan))
Dan tes dapat ditulis untuk memastikan bahwa itu berfungsi dengan benar. Saya menggunakan NUnit dan Moq tetapi kerangka uji apa pun akan dilakukan
Pola ini akan bekerja untuk setiap fungsi yang tergantung pada faktor-faktor eksternal, seperti
System.Random.Next()
,System.DateTime.Now.UtcNow
,System.Guid.NewGuid()
dllLihat https://loadlimited.visualstudio.com/Stamina/_git/Stamina.Core untuk contoh lebih lanjut atau dapatkan https://www.nuget.org/packages/Stamina.Core paket nuget.
sumber
Anda dapat mengubah kelas yang Anda uji untuk menggunakan
Func<DateTime>
yang akan melewati parameter konstruktornya, jadi ketika Anda membuat instance dari kelas dalam kode nyata, Anda bisa meneruskan() => DateTime.UtcNow
keFunc<DateTime>
parameter, dan pada tes, Anda bisa menghabiskan waktu Anda ingin menguji.Sebagai contoh:
sumber
Saya mendapat masalah yang sama, tetapi saya berpikir kita tidak boleh menggunakan set datetime hal-hal di kelas yang sama. karena bisa mengakibatkan penyalahgunaan suatu hari nanti. jadi saya telah menggunakan provider seperti
Untuk tes, pada proyek uji dibuat penolong yang akan berurusan dengan hal-hal yang ditetapkan,
pada kode
dan pada tes
Tetapi perlu mengingat satu hal, kadang-kadang DateTime yang asli dan DateTime penyedia tidak bertindak sama
Saya berasumsi penghormatan akan menjadi TimeSpan.FromMilliseconds maksimum (0,00002) . Tetapi sebagian besar waktu bahkan lebih sedikit
Temukan sampel di MockSamples
sumber
Ini jawaban saya untuk pertanyaan ini. Saya menggabungkan pola 'Ambient Context' dengan IDisposable. Jadi Anda bisa menggunakan DateTimeProvider.Current dalam kode program normal Anda dan dalam tes Anda menimpa ruang lingkup dengan pernyataan menggunakan.
Berikut adalah cara menggunakan DateTimeProvider di atas di dalam Unit-Test
sumber
Menggunakan
ITimeProvider
kami terpaksa membawanya ke proyek bersama umum khusus yang harus dirujuk dari sisa proyek lainnya. Tetapi ini mempersulit kontrol ketergantungan .Kami mencari
ITimeProvider
di .NET framework. Kami mencari paket nuget, dan menemukan satu yang tidak bisa bekerja denganDateTimeOffset
.Jadi kami datang dengan solusi kami sendiri, yang hanya bergantung pada jenis perpustakaan standar. Kami menggunakan contoh dari
Func<DateTimeOffset>
.Cara Penggunaan
Bagaimana cara mendaftar
Autofac
( Untuk editor mendatang: tambahkan kasus Anda di sini ).
Cara tes unit
sumber
Mungkin kurang Profesional tetapi solusi yang lebih sederhana dapat membuat parameter DateTime di metode konsumen. Misalnya alih-alih membuat metode seperti SampleMethod, buat SampleMethod1 dengan parameter. Menguji SampleMethod1 lebih mudah
sumber