开发者

Unit testing, statics, and factories

开发者 https://www.devze.com 2023-02-25 13:54 出处:网络
I am implementing a model in Java which requires iterating over a collection and going through a number of identification stages, it involves for loops, while loops etc. It is the sort of thing I want

I am implementing a model in Java which requires iterating over a collection and going through a number of identification stages, it involves for loops, while loops etc. It is the sort of thing I want to test at a fine-grained level so that I have confidence it has been implemented properly.

I have used it as an opportunity to start unit testing, as it is something I recognise as being beneficial to my code. I have since been reading a forest of books to get up to speed with JUnit and unit testing.

Basically my question comes down to two conflicting pieces of advice I have received:

1) Statics are evil. Do not touch statics. Do not test privates either, you probably want a class there instead.

2) Use factories for creation to allow dependency injection using parameters - potentially allowing use of mocks and stubs for isolation.

In my example I am looking to perform an operation along the lines of:

double height = 223.42; // this was set iterating over a collection of doubles
//blah
HeightBounds b = HeightBounds.getHeightBounds开发者_运维知识库(height);
//more blah

I have done this in order to avoid building up what would become a very long and complicated block of code, which I can only test in its entirety. This way I have public accessible objects that I can test to ensure the system components all perform correctly.

Common sense says to me there is nothing wrong with static factories, and that they are easily tested, but am I missing something blindingly obvious given I am learning test-driven design?

Thank you


The static factory class introduces coupling between your class and the HeightBounds class. this might make your class difficult to test, if for example HeightBounds goes off and looks in DB for information, or reads from a web service etc etc.

If you instead injected an IHeightBounds implementation into your class then you could mock that out so you could test what happens when the dependencies of you class do certain things.

For example what if HeightBounds throws an exception? or returns null? Or you want to test when a particular HeightBound is returned? With an interface it is easy to mock this behaviour, with a static factory it is more difficult as you have manufacture the data to create the desired results in the class.

You could still only have a single implementation of HeightBounds and would be able to test that in isolation, but you would be able to test your method above without even having an real implementation.

I would probably have an IHeightBoundFactory interface and inject an implementation into the class.

As for testing the privates, generally you don't want to. You want to be testing one of 2 things, either that the results are what you expected or that the interactions are what you expected.

If you have a method called Add and a method called GetAll then you might want to test that when you call Add and then call GetAll you get back the one you added. you don't care how this is implemented, just that it works. this is testing the results. Generally in this situation you want to create mock objects that return data. This seems to be your situation.

If when you call Add you expect that what is being added is logged, then you want to test the interactions with the logging dependency, so you inject a mock dependency and verify that the interaction with that class happened when you called Add. Generally in this situation you want to create mock objects that have expectations set, and you verify that those expectations have been met. It doesn't look this is necessary in the situation you described above.


Statics are evil. Do not touch statics.

"static" here probably means Singletons, i.e. global state. That is indeed difficult to use in unit tests, and can introduce lots of subtle issues, so it is better to avoid it in general. However, static members (fields, methods or inner classes) in general are not necessarily a problem per se.

Do not test privates either, you probably want a class there instead.

This is true in general, if you feel the need to test a private method, it is a sign that your API is not fine grained enough, and the enclosing class may be trying to do too much. Often you may identify some coherent group of functionality which is better to extract into a separate class. This improves your design and in turn makes unit testing easier.


A problem with static factories is, that you are not able to replace the factories (and sometime the objects created by the factories) by mocks. - That is one reason why IOC Containers are so usefull.


Static methods basically kill unit testing.

One of the basic ideas when you want to unit test your application is the ability to isolate different portions of your code. You usually do this by wiring mock objects for your environment. You can't wire anything if you are using static methods. Moreover, using static methods hides dependencies between objects.

In your last paragraph you say you want to learn test-driven design. If you are writing your tests after you've written your code your tests are not actually driving anything. Write your tests first.

Regarding private methods, you would usually have full test coverage of the public methods that use those private methods, so you are going to cover them anyway. If you have a really complex private method that you want to test (you should not) just use reflection to make it accessible. I'd rather break encapsulation than having untested code.

Misko Hevery has a very nice post on this topic.


Static methods are not evil per se. Look in JDK how many of them are there. Static methods also need to be unit tested, so if you have any go on and test them.

So it's not true that static methods kill unit testing.

But if you make statics only to write less code in unit tests then it is a wrong path since as others have stated you should better use your objects using your usual API - since this is what you want to test.

And full ACK for writing tests first - it will also help you design better API, cut you classes right.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号