很抱歉在这篇文章中出现了死灵,但我觉得有必要对一些似乎没有涉及到的事情进行权衡。
First a foremost - when we find ourselves needing access to private members on a class during unit testing, it is generally a big, fat red flag that we've goofed in our strategic or tactical approach and have inadvertently violated the single responsibility principal by pushing behavior where it does not belong. Feeling the need to access methods that are really nothing more than an isolated subroutine of a construction procedure is one of the most common occurrences of this; however, it's kind of like your boss expecting you to show up for work ready-to-go and also having some perverse need to know what morning routine you went through to get you into that state...
发生这种情况的另一个最常见的实例是当您发现自己试图测试众所周知的“神类”时。这本身是一种特殊的问题,但同样的基本问题是需要知道手术的详细信息——但这就跑题了。
在这个特定的例子中,我们有效地将完全初始化Bar对象的责任分配给了FooBar类的构造函数。在面向对象编程中,一个核心原则是构造函数是“神圣的”,应该防范无效数据,这些无效数据会使构造函数自己的内部状态失效,并使它在下游的其他地方(可能是一个非常深的管道)失败。
在这里,我们没有做到这一点,因为我们允许FooBar对象接受一个在构造FooBar时还没有准备好的Bar,并通过某种“hack”FooBar对象来补偿它,让它自己来处理问题。
This is the result of a failure to adhere to another tenent of object oriented programming (in the case of Bar,) which is that an object's state should be fully initialized and ready to handle any incoming calls to its' public members immediately after creation. Now, this does not mean immediately after the constructor is called in all instances. When you have an object that has many complex construction scenarios, then it is better to expose setters to its optional members to an object that is implemented in accordance with a creation design-pattern (Factory, Builder, etc...) In any of the latter cases, you would be pushing the initialization of the target object off into another object graph whose sole purpose is directing traffic to get you to a point where you have a valid instance of that which you are requesting - and the product should not be considered "ready" until after this creation object has served it up.
在你的例子中,Bar的“status”属性似乎不在一个FooBar可以接受的有效状态中——所以FooBar对它做了一些事情来纠正这个问题。
The second issue I am seeing is that it appears that you are trying to test your code rather than practice test-driven development. This is definitely my own opinion at this point in time; but, this type of testing is really an anti-pattern. What you end up doing is falling into the trap of realizing that you have core design problems that prevent your code from being testable after the fact, rather than writing the tests you need and subsequently programming to the tests. Either way you come at the problem, you should still end up with the same number of tests and lines of code had you truly achieved a SOLID implementation. So - why try and reverse engineer your way into testable code when you can just address the matter at the onset of your development efforts?
如果您这样做了,那么您将更早地意识到,为了测试您的设计,您将不得不编写一些相当繁琐的代码,并且将有机会在早期通过将行为转移到易于测试的实现来重新调整您的方法。