Конструктор для класса Download
запускает загрузку, а это значит, что я не могу подписаться на наблюдателя, пока не начнется загрузка.Это состояние гонки.Возможно (хотя и маловероятно), что наблюдатели будут уведомлены, прежде чем они могут быть подписаны.
public Download(string url)
{
this.url = url;
startDownload();
}
Но я могу пойти дальше и проверить, потому что я подписываюсь на наблюдателя, прежде чем это произойдет.Если вы можете, я бы рекомендовал не делать этого.Разрешить вызывающей стороне создать класс за один шаг, а затем начать загрузку с помощью вызова метода.
Мне также пришлось изменить этот метод.Я полагал, что тестирование на ошибку было бы самым простым первым шагом, но он должен сделать data = e.Result
, если есть ошибка no , а не если есть ошибка.
private void StartDownload()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) =>
{
if (e.Error == null) // <== because of this
{
data = e.Result;
}
closed = true;
sendAndComplete();
});
client.DownloadStringAsync(new Uri(url));
}
Что я не ожидал, так это то, что WebClient.DownloadStringAsync
на самом деле не асинхронный.Он не возвращает Task
.Это просто требует обратного вызова.Это означает, что нет точного способа узнать, сделано ли это, кроме как ждать, пока он уведомит наблюдателя о завершении загрузки.
Мой тестовый прогон NUnit не работал, поэтому я использовал MsTest.Это то же самое.
Основной подход заключается в том, что я создаю некоторые флаги, а инспектор отвечает на уведомления, устанавливая флаги.Таким образом, я могу видеть, какие уведомления были получены.
Последняя проблема заключается в том, что, поскольку DownloadStringComplete
является обратным вызовом, тест завершается до Assert
.Это означает, что это всегда пройдет.Поэтому, чтобы исправить это, я должен был сделать что-то, чего я никогда раньше не видел, и нашел здесь :
[TestMethod]
public void download_raises_error_notification()
{
var success = false;
bool error = false;
bool complete = false;
var pause = new ManualResetEvent(false);
var download = new Download("http://NoSuchUrlAnywhere.com");
var inspector = new DownloadInspector(
onSuccessAction: s => success = true,
onCompleteAction: () =>
{
complete = true;
pause.Set();
},
onErrorAction: s => error = true
);
download.Subscribe(inspector);
// allow 500ms for the download to fail. This is a race condition.
pause.WaitOne(500);
Assert.IsTrue(error,"onErrorAction was not called.");
}
Это технически интеграционный тест, так как он действительно должен пытатьсяскачать, чтобы запустить.Это можно исправить с помощью насмешки WebClient
.