Birim testi için eşzamansız bir yöntemi alay etmek için Adedi kullanma


180

Web APIaraması yapan bir hizmet için bir yöntem test ediyorum . HttpClientWeb hizmetini (çözümdeki başka bir projede bulunur) yerel olarak da çalıştırırsam, birim testleri için normal çalışır.

Ancak değişikliklerimi kontrol ettiğimde yapı sunucusunun web hizmetine erişimi olmayacak, bu yüzden testler başarısız olacak.

Bir IHttpClientarayüz oluşturarak ve uygulamamda kullandığım bir sürümü uygulayarak birim testlerim için bir yol tasarladım . Birim testleri için, alaylı bir sürümü alay edilmiş bir asenkron post yöntemiyle tamamlıyorum. İşte burada sorunlarla karşılaştım. HttpStatusResultBu test için bir TAMAM dönmek istiyorum . Başka bir benzer test için kötü bir sonuç döndüreceğim.

Test devam edecek ancak asla tamamlanmayacak. Bekliyor. Eşzamansız programlama, delegeler ve Adedi'nin kendisi için yeniyim ve bir süredir yeni şeyler öğrenirken SO ve google'ı araştırıyorum, ancak yine de bu sorunu aşamıyorum.

İşte test etmeye çalıştığım yöntem:

public async Task<bool> QueueNotificationAsync(IHttpClient client, Email email)
{
    // do stuff
    try
    {
        // The test hangs here, never returning
        HttpResponseMessage response = await client.PostAsync(uri, content);

        // more logic here
    }
    // more stuff
}

İşte benim birim test yöntemi:

[TestMethod]
public async Task QueueNotificationAsync_Completes_With_ValidEmail()
{
    Email email = new Email()
    {
        FromAddress = "bob@example.com",
        ToAddress = "bill@example.com",
        CCAddress = "brian@example.com",
        BCCAddress = "ben@example.com",
        Subject = "Hello",
        Body = "Hello World."
    };
    var mockClient = new Mock<IHttpClient>();
    mockClient.Setup(c => c.PostAsync(
        It.IsAny<Uri>(),
        It.IsAny<HttpContent>()
        )).Returns(() => new Task<HttpResponseMessage>(() => new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

    bool result = await _notificationRequestService.QueueNotificationAsync(mockClient.Object, email);

    Assert.IsTrue(result, "Queue failed.");
}

Neyi yanlış yapıyorum?

Yardımın için teşekkürler.

Yanıtlar:


351

Bir görev yaratıyorsunuz ama asla başlamıyorsunuz, bu yüzden asla tamamlanmıyor Ancak, sadece görevi başlatmayın - bunun yerine, Task.FromResult<TResult>size zaten tamamlanmış bir görev verecek olanı kullanın:

...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

Gerçek eşzamansızlığı bu şekilde test etmeyeceğinizi unutmayın - eğer bunu yapmak Task<T>istiyorsanız, daha hassas bir şekilde kontrol edebileceğiniz bir tane oluşturmak için biraz daha fazla iş yapmanız gerekir ... başka bir gün.

Ayrıca IHttpClienther şeyi alay etmek yerine sahte kullanmayı düşünebilirsiniz - bu gerçekten ne sıklıkla ihtiyacınız olduğuna bağlıdır.


2
Çok teşekkür ederim. Harika çalıştı. Muhtemelen anlamadığım basit bir şey olduğunu düşündüm.
mvanella

2
Re: Sahte IHttpClient, ben bunu düşündüm ama web API geri gelen beklenen davranış dayalı farklı testler için farklı HttpStatusCodes döndürmek gerekiyordu, ve bu bana daha fazla kontrol vermek gibi görünüyordu.
mvanella

3
@mvanella: Evet, böylece istediğin her şeyi geri getirebilecek bir sahte yaratırsın. Sadece düşünecek bir şey.
Jon Skeet

134
Bunu şimdi bulan herkes için, Moq 4.2 adlı bir uzantıya sahiptir ReturnsAysnc, bu da tam olarak bunu yapar.
Stuart Grassie

3
@legacybass API dokümanları neredeyse bir yıl önce piyasaya sürülen v4.2.1312.1622'ye karşı oluşturulduklarını söylese de, bunun için herhangi bir belgeye bağlantı bulamıyorum . Bkz bu taahhüt birkaç gün o yayınlanmadan önce yapıldığı. API belgelerinin neden güncellenmediğine
gelince

17

Yukarıdaki @Stuart Grassie'nin cevabını önerin.

var moqCredentialMananger = new Mock<ICredentialManager>();
moqCredentialMananger
                    .Setup(x => x.GetCredentialsAsync(It.IsAny<string>()))
                    .ReturnsAsync(new Credentials() { .. .. .. });

1

İle Mock.Of<...>(...)için asynckullanabileceğiniz yönteme Task.FromResult(...):

var client = Mock.Of<IHttpClient>(c => 
    c.PostAsync(It.IsAny<Uri>(), It.IsAny<HttpContent>()) == Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))
);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.