Модульный тестовый блок TPL LinkTo TargetBlock
Я пытаюсь создать модульные тесты для TPL BufferBlock и хочу проверить, что генерируется исключение. Однако тест проходит до того, как будет сгенерировано исключение.
РЕДАКТИРОВАТЬ
Кроме того, это длительный процесс, поэтому я не называю завершенным. Этот процесс выполняется, пока приложение не будет закрыто
Вот код:
public class PriorityMessageQueue
{
private BufferBlock<MyMessage> _messageBufferBlock;
private async Task<bool> Init()
{
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
BoundedCapacity = 1000
};
var prioritizeMessageBlock = new ActionBlock<MyMessage>(msg =>
{
try
{
SetMessagePriority(msg);
}
catch (Exception)
{
throw;
}
});
_messageBufferBlock = new BufferBlock<MyMessage>(executionDataflowBlockOptions);
_messageBufferBlock.LinkTo(prioritizeMessageBlock);
}
public async Task<bool> EnqueueAsync(MyMessage message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message), "The message object is NULL. Cannot enqueue a NULL object.");
}
return await _messageBufferBlock.SendAsync(message);
}
private void SetMessagePriority(MyMessage message)
{
if (message.MessageType.Equals(MessageType.Unknown))
{
throw new ArgumentException("The SCBA Message Type is 'Unknown'. Cannot set the Message Priority on an 'Unknown' message type.");
}
switch (message.MessageType)
{
case MessageType.Admin:
message.MessagePriority = MessagePriority.Admin;
break;
case MessageType.AuthUser:
message.MessagePriority = MessagePriority.High;
break;
case MessageType.UnknownUser:
message.MessagePriority = MessagePriority.Normal;
break;
default:
message.MessagePriority = MessagePriority.Normal;
break;
}
}
}
Вот тестовый код
[TestClass()]
public class PriorityMessageQueueTests
{
private IPriorityMessageQueue _queue;
[TestInitialize]
public void Init()
{
IUnityContainer container = new UnityContainer();
var logRepository = new Mock<ILogRepository>();
container.RegisterInstance(logRepository.Object);
_queue = new PriorityMessageQueue(logRepository.Object);
}
[TestCleanup]
public void CleanUp()
{
_queue.Dispose();
}
[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public async Task EnqueueTest_NullMessage()
{
await _queue.EnqueueAsync(null);
}
[TestMethod()]
public async Task EnqueueTest_UnknownMessageType()
{
var message = new MyMessage
{
Id = 1,
Text = "Unit Test"
};
var result = await _queue.EnqueueAsync(message);
Assert.AreEqual(true, result);
}
[TestMethod()]
public void DequeueTest()
{
Assert.Fail();
}
[TestMethod()]
public void GetNextInQueue()
{
Assert.Fail();
}
}
Исключение ArgumentException правильно генерируется в SetMessagePriority, потому что "MessageType" равен MessageType.Unknown. Тем не менее, к моменту создания ArgumentException модульный тест EnqueueTest_UnknownMessageType успешно прошел, поскольку
var result = await _queue.EnqueueAsync(message);
возвращает true до того, как сгенерировано исключение. Как мне написать тест EnqueueTest_UnknownMessageType, чтобы он не выполнялся, потому что генерируется исключение?
Я пробовал добавлять
[ExpectedException(typeof(ArgumentException))]
к тесту, но он все еще проходит
1 ответ
Как отметил @JSteward, исключение, которое вы ожидаете увидеть, не будет частью Task
которую вы ждете в EnqueueAsync()
метод.
Читая документы для DataflowBlock.SendAsync()
говорит следующее о возвращаемом значении:
Если цель принимает и использует предложенный элемент во время вызова
SendAsync
по возвращении из звонкаTask<TResult>
будет завершено, и егоResult
собственность вернетсяtrue
, Если цель отклоняет предложенный элемент во время вызова, при возврате из вызова получаетсяTask<TResult>
будет завершено, и егоResult
собственность вернетсяfalse
,
Task
только передает, что сообщение было получено или отклонено первым блоком. Нет упоминания о распространении исключений из последующих блоков обратно в оригинал Task
,
Если вы ищете раннюю проверку типа сообщения, вы можете сделать эту проверку прямо в EnqueueAsync()
вызов, удалив этот блок из вашего потока данных.
В противном случае, если вы хотите, чтобы это был шаг в более крупном потоке данных, вы можете добавить BroadcastBlock<T>
к которому вы можете присоединить остальную часть конвейера "счастливого пути" для обработки известных типов сообщений, а также блок, который принимает только неизвестные типы сообщений, где вы можете предпринять любое действие, которое вы хотели бы сообщить, был получен неизвестный тип сообщения.