2011-06-03 94 views
3

于是我沿着这我应该如何测试访问私有成员的公共方法

private List<ConcurrentQueue<int>> listOfQueues = new List<ConcurrentQueue<int>>() 
public void InsertInt(int id, int value) 
{ 
    listOfQueues[id].Enqueue(value); 
} 

线的东西这是不是我不应该是单元测试?
如果是我如何测试InsertInt方法而不使用任何其他方法? 是否可以使用该方法从队列中检索数据以测试它是否正确输入? 这是我应该使用模拟的东西吗?

回答

2

你一般不想测试私人成员。单元测试的重点在于,从外部开始,课程将采用指定的输入,并且(在需要时)将为您提供正确的输出。您不关心如何为您提供这些输出的实现,您只是在意它为您提供正确的外部使用输出。

为了表明这一点,假设您创建了一个单元测试,验证您的InsertInt()方法将int插入到listOfQueues。然后您的需求会发生变化,您必须更改实施策略,而不是使用List<ConcurrentQueue<int>>,它会变成Dictionary<string, ConcurrentQueue<int>>。这实际上可能不需要对输入进行更改,并且您的输出仍然可以通过任何输出验证,但是您的单元测试将失败,因为它被硬编码为实施。

更好的办法是进行单元测试,确保如果用正确的输入调用InsertInt(),您的输出方法仍然会返回已更正的输出,并且创建使用无效参数调用InsertInt()的单元测试会导致异常。然后,您可以更改有关您内部实施的所有内容,并确信您的班级仍能正常工作。单元测试的实施增加了额外的开销,同时在可测试性方面提供了很少的好处。

请注意,我并不是说这个方法不应该被单元测试,它只是单元测试需要以反映外部对象如何与你的类交互的方式来开发。

+0

所以它更好在一个单元测试中测试多个东西,而不是将单元测试与实现联系起来? – scott 2011-06-03 13:05:41

+0

是的,我相信是这样,因为它模仿了真实世界中如何使用这个类。如果你独立地测试这两种方法,你就没有一个单元测试来确保输入和输出方法实际上在一起工作,这只是一个假设。然后,您可以更改其中一个的实现(和测试),而您的单元测试将通过,但代码将在生产中失败。你仍然需要进行单元测试,如果你用x参数调用输入方法,你的输出会给你y个参数,因此私人测试是浪费时间和精力。 – KallDrexx 2011-06-03 13:12:19

+0

此外,它仍然是单元测试,因为你的单元测试只处理一个类本身而不是在类之外,所以处理多种方法并不是一件坏事。 – KallDrexx 2011-06-03 13:13:14

1

由于此方法是一种公共方法,因此需要对其进行单元测试。

快速查看一下你的方法会发现,通过-1作为id的值会导致ArgumentOutOfRangeException。如果已经设计了此方法的单元测试用例(假设存在许多此类方法),那么在编码期间就会实现这一点。

要检查插入是否成功,可以使用@Oskar Kjellin指出的方法。

如果你想弄脏,那么你可以使用Reflection来检查值是否已经被插入。

// Sample with Queue instead of ConcurrentQueue 
private void TestInsertInt(int id, int value) 
{ 
    myInstance.InsertInt(id, value); 

    FieldInfo field = myInstance.GetType().GetField("listOfQueues", BindingFlags.NonPublic | BindingFlags.Instance); 
    List<Queue<int>> listOfQueues = field.GetValue(myInstance) as List<Queue<int>>; 
    int lastInserted = listOfQueues[id].Last(); 

    Assert.AreEqual(lastInserted, value); 
} 
+0

这就是我的感受。所以我想更重要的问题是做这件事的正确方法是什么? – scott 2011-06-03 12:42:03

+0

你不需要单元测试私人成员来创建一个单元测试,传递一个零的ID将导致一个'ArgumentOutOfRangeException' – KallDrexx 2011-06-03 12:47:39

2

是的,你应该单元测试它。您可以使用私人访问器来获取listOfQueues。 您必须确保使用单元测试,该方法的行为与例外情况相同,并且确实插入了该项目。

看看这篇文章对如何单元测试的私有方法http://msdn.microsoft.com/en-us/library/ms184807(v=vs.80).aspx

+0

以下文章列出将导致Visual Studio询问你是否想添加一个私人accesser到部件。您可以通过将以下行添加到您的AssemblyInfo.cs页面[assembly:System.Runtime.CompilerServices.InternalsVisibleTo(“”)]手动执行此操作。 – JMcCarty 2011-06-03 12:44:51

+0

InternalsVisibleTo仅使内部可见 - 不是私有的?上面的文章生成私有方法的反射包装,然后您可以将其编译到您的测试程序集中。 – 2011-06-03 12:47:02

+0

有没有办法为会员生成私有访问器而不是方法? – scott 2011-06-03 13:04:30

0

这是不是我不应该是单元测试 ?

如果你的目标100%的测试覆盖率则是

如果是我怎么测试,而无需使用任何其他 方法InsertInt 方法?

有几个选项:

  • 使用不同的访问方法, 你的建议。
  • 使容器内,并给予单元测试组件访问内部构件
  • 因素容器伸到你传递作为一个依赖于这个类,然后在你的单元测试通过一个模拟的容器,而不是
  • 一个单独的类使用反射访问私人会员
0

另一个黑客是使用PrivateObject。个人而言,我会使用DI(依赖注入),或者为了单元测试而使用private,internal

1

您不应该测试队列的行为 - 这是您可以在不更改方法行为的情况下更改的实现细节。例如,您可以将ConcurrentQueue替换为其他数据结构(可能是树),而无需更新单元测试。

您正在测试的是此方法接受输入并按您的预期存储值。因此如

public int GetInt(int id) 

然后你测试的方法插入你希望使用相同的ID检索它们,你需要询问系统状态的某种方式。

您应该测试公共方法返回所有您可以想到的结果,并将其留在方法中,以便按照它认为合适的方式存储值。因此,我可能会测试这样的方法,以不同的输入:

 [TestCase(1,2,3)] // whatever test cases make sense for you 
     [TestCase(4,5,6)] 
     [TestCase(7,8,9)] 
     [Test] 
     public void Test_InsertInt_OK(int testId, int testValue, int expectedValue) 
     { 
      InsertInt(testId, testValue); 
      Assert.AreEqual(GetInt(testId), expectedValue) 
     } 
相关问题