how to avoid returning mocks from a mocked object list

匿名 (未验证) 提交于 2019-12-03 02:51:02

问题:

I'm trying out mock/responsibility driven design. I seem to have problems to avoid returning mocks from mocks in the case of objects that need a service to retrieve other objects.

An example could be an object that checks whether the bills from last month are paid. It needs a service that retrieves a list of bills. So I need to mock that billRetrievalService in my tests. At the same time I need that BillRetrievalMock to return mocked Bills (since I don't want my test to rely on the correctness of the Bill implementation).

Is my design flawed? Is there a better way to test this? Or is this the way it will need to be when using finder objects (the finding of the bills in this case)?

side note: althout Bill might be a value object candidate, the wider problem still remains when the collections aren't containing value objects (eg Users).

回答1:

Most of the time, if I need a mock to return another mock, I find a dependency that makes more sense in the other direction. Stated differently, mock-returning-mock usually points to a violation of the Dependency Inversion Principle.

One common exception: a factory that creates objects (as opposed to a "holder" that simply returns the same object each time). If I need to create multiple objects of the same type during my lifetime, then I might need to depend on an ObjectFactory and invoke #createObject(), then perhaps set expectations on the Objects. Even so, I would question this. It might be possible for something else one level up the call stack to create Objects for me and give them to me as needed.

In the ObjectHolder case, rather than depending on the ObjectHolder to get the Object, I prefer to depend on the Object directly and force my caller to give it to me however it wants. This respects the desirable design property of context independence.

One specific version of this issue is the "Virtual Clock" pattern. Sometimes you need to depend on virtual clock, but often it's better simply to demand a timestamp. ("Instantaneous Request" pattern.)



回答2:

Mock returning mocks is a strong code smell - a possible problem with the design. It could be that the Bills should be immutable values objects which should not be mocked. Or there is some confusion with the design and the responsibilities of the classes.

The book Growing Object-Oriented Software, Guided by Tests and paper Mock Roles, not Objects from the inventors of mock objects are worth reading.



回答3:

Usually when I mock I end up having a triad of objects. The first object would be a coordinator BillsPaidLastMonthCoordinator this object has two dependencies BillRetrievalService and BillPaidValidator.

You would mock the two dependencies and your test would be for the interaction of retrieval and passing bills to the validation. So for this test you will not care what the data is. This helps separate responsibilities. Your original object was responsible for retrieving Bills and then seeing if it was a isPaid Bill.

With the way you described the problem you can end up is a noisy and brittle test. The brittleness comes from it being able to be broken in two ways.

With the corrdinator, it doesn't have to change if Bill implementation changes, Just the objects that actually use a Bill. My 2centavos.

[EDIT]

This is more aligned with using event handlers (the coordinator)



回答4:

As the Way of the Testuvius advices, no principle, however good, should be taken absolutely and so it is also with the rule that you shouldn't need mocks returning mocks, there are cases when this is quite suitable.

As Gutzofter suggests, you could break your object into two, one for the actual validation and another one for retrieval of the bills to validate. The advantage of this "single responsibility only" principle application is that the validator would be more generic and reusable. On the other hand, if you only have this simple use case and no special need for the higher reusability, it's a very pragmatic to keep the retrieval and validation in a single class. Layering, explosion of the number of objects etc. unjustified by a real need and a real benefit, done only for the sake of satisfying an abstract principle, isn't good. You always have to weight the pros and cons and the reality rarely is simple and as beautiful as we would like :-) Great examples of this pragmatic approach are in Adam Bien's Real World Java EE Patterns - Rethinking Best Practices.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!