我已经创建了一个类SenderClass,它将从它的构造函数中启动和运行一个后台工作人员。方法RunWorker()是一个while(true)循环,它将弹出队列中的元素,通过方法SendMessage()发送它们,并睡眠一小段时间,以便将新元素添加到队列中。
问题在于:如何测试从队列发送元素的方法,而不将其公开给使用类的人?
执行情况:
public class SenderClass : ISenderClass
{
private Queue<int> _myQueue = new Queue<int>();
private Thread _worker;
public SenderClass()
{
//Create a background worker
_worker = new Thread(RunWorker) {IsBackground = true};
_worker.Start();
}
private void RunWorker() //This is the background worker's method
{
while (true) //Keep it running
{
lock (_myQueue) //No fiddling from other threads
{
while (_myQueue.Count != 0) //Pop elements if found
SendMessage(_myQueue.Dequeue()); //Send the element
}
Thread.Sleep(50); //Allow new elements to be inserted
}
}
private void SendMessage(int element)
{
//This is what we want to test
}
public void AddToQueue(int element)
{
Task.Run(() => //Async method will return at ones, not slowing the caller
{
lock (_myQueue) //Lock queue to insert into it
{
_myQueue.Enqueue(element);
}
});
}
}通缉界面:
public interface ISenderClass
{
void AddToQueue(int element);
}测试所需的接口:
public interface ISenderClass
{
void SendMessage(int element);
void AddToQueue(int element);
}有一个非常简单的解决方案,说我创建的类由于单响应原理而不正确,我的类的目的不是发送消息,而是实际运行发送消息的内容。
我应该拥有的是另一个类TransmittingClass,它通过自己的接口公开方法SendMessage(int)。这样我就可以测试这个类了,SenderClass应该通过这个接口调用这个方法。
但是,对于当前的实现,我还有其他选择吗?
我可以让我想测试的所有私有方法(所有这些方法)都有一个[assembly:InternalsVisibleTo("MyTests")],但是是否存在第三个选项?
发布于 2015-12-13 15:22:50
发送消息逻辑应该在具有单独接口的单独类中实现。这个类应该将新类作为依赖项。您可以单独测试新类。
public interface IMessageQueue
{
void AddToQueue(int element);
}
public interface IMessageSender
{
void SendMessage(object message);
}
public class SenderClass : IMessageQueue
{
private readonly IMessageSender _sender;
public SenderClass(IMessageSender sender)
{
_sender = sender;
}
public void AddToQueue(int element)
{
/*...*/
}
private void SendMessage()
{
_sender.SendMessage(new object());
}
}
public class DummyMessageSender : IMessageSender
{
//you can use this in your test harness to check for the messages sent
public Queue<object> Messages { get; private set; }
public DummyMessageSender()
{
Messages = new Queue<object>();
}
public void SendMessage(object message)
{
Messages.Enqueue(message);
//obviously you'll need to do some locking here too
}
}编辑
为了解决您的评论,下面是一个使用Action<int>的实现。这允许您在测试类中定义消息发送操作,以模拟SendMessage方法,而不必担心创建另一个类。(就我个人而言,我仍然倾向于显式地定义类/接口)。
public class SenderClass : ISenderClass
{
private Queue<int> _myQueue = new Queue<int>();
private Thread _worker;
private readonly Action<int> _senderAction;
public SenderClass()
{
_worker = new Thread(RunWorker) { IsBackground = true };
_worker.Start();
_senderAction = DefaultMessageSendingAction;
}
public SenderClass(Action<int> senderAction)
{
//Create a background worker
_worker = new Thread(RunWorker) { IsBackground = true };
_worker.Start();
_senderAction = senderAction;
}
private void RunWorker() //This is the background worker's method
{
while (true) //Keep it running
{
lock (_myQueue) //No fiddling from other threads
{
while (_myQueue.Count != 0) //Pop elements if found
SendMessage(_myQueue.Dequeue()); //Send the element
}
Thread.Sleep(50); //Allow new elements to be inserted
}
}
private void SendMessage(int element)
{
_senderAction(element);
}
private void DefaultMessageSendingAction(int item)
{
/* whatever happens during sending */
}
public void AddToQueue(int element)
{
Task.Run(() => //Async method will return at ones, not slowing the caller
{
lock (_myQueue) //Lock queue to insert into it
{
_myQueue.Enqueue(element);
}
});
}
}
public class TestClass
{
private SenderClass _sender;
private Queue<int> _messages;
[TestInitialize]
public void SetUp()
{
_messages = new Queue<int>();
_sender = new SenderClass(DummyMessageSendingAction);
}
private void DummyMessageSendingAction(int item)
{
_messages.Enqueue(item);
}
[TestMethod]
public void TestMethod1()
{
//This isn't a great test, but I think you get the idea
int message = 42;
_sender.AddToQueue(message);
Thread.Sleep(100);
CollectionAssert.Contains(_messages, 42);
}
}发布于 2015-12-13 15:06:34
看起来SenderClass根本不应该执行任何发送。它应该简单地维护队列。通过执行发送的构造函数注入一个Action<int>。这样,您可以将SendMessage移到其他地方,并将其命名为您喜欢的任何地方。
作为一个额外的好处,您对SendMessage的测试并没有混乱的队列管理。
看到您的编辑,您似乎不喜欢这种方法,您似乎也不喜欢InternalsVisibleTo方法。您可以通过一个单独的接口公开SendMessage并显式地实现该接口。这样,SendMessage仍然可以通过该接口调用,但在默认情况下,如果没有一些转换操作,就无法访问它。它也不会出现在intellisense自动完成列表中。
https://stackoverflow.com/questions/34252400
复制相似问题