Designing constructors to test

I work with some existing code, trying to add to it and increase unit tests for it. But some problems with getting the code can be checked.

Original constructor:

public Info() throws Exception { _ServiceProperties = new ServiceProperties(); _SshProperties = new SshProperties(); } 

I know this is bad, and obviously not verifiable. In the junit environment, this class will not be able to create each time, since it will not be able to find the necessary properties for self-creation. Now I know that this class will be much more susceptible to verification with a simple replacement of something preceded by a "new" as a parameter.

So, I get:

New constructor:

 public Info(ServiceProperties srvProps, SshProperties sshProps) throws Exception { _ServiceProperties = srvProps; _SshProperties = sshProps; } 

Which allows me to correctly unit test this Info class. However, the problem is that now all this work is being transferred to some other class:

Method of another class:

 public void useInfo() throws Exception { ServiceProperties srvProps = new ServiceProperties(); SshProperties sshProps = new SshProperties(); Info info = new Info(srvProprs, sshProprs); doStuffWithInfo(info); } 

Now this method cannot be verified. All I managed to do was push away where the constructions of these Property objects take place, and somewhere else some piece of code will depend on the need to be called "new."

They are rubbing me here: I can’t understand how to break this chain of events by simply pressing these “new” calls elsewhere. What am I missing?

+6
java constructor unit-testing refactoring
source share
6 answers

Look at using an Injection Dependency framework like Spring . This Inversion of Control application means that each of your types can avoid the error you saw by leaving the configuration with wired components together.

This introduction to Spring (pdf) provides a comprehensive overview of Spring. The first chapter or two should be sufficient to explain the concepts.

Also see Inversion of control containers and Martin Fowler dependency injection pattern

+7
source share

You have the right idea. Perhaps this will help you. I recommend that you follow two rules for all of your significance classes, where “significance” means that if you don't follow the steps, it will be harder to test, reuse, or maintain the class. Here are the rules:

  • never create or acquire addiction on your own
  • always works with interfaces

You have a beginning in rule number 1. You have modified your Info class so that you no longer create its dependencies. By “dependency” I mean other classes, configuration data loaded from property files, or something else, etc. When you depend on how something is created, you associate your class with it and make it difficult to test, reuse, and maintain it. Thus, even if a dependency is created using factory or singleton, do not create its class. Ask for something else to call create() or getInstance() or something else and pass it.

So you chose “something else” as the class that your class uses and realized that it has a bad smell. The tool is to instead have your entry point into your application instantiate all the dependencies. In a traditional java application, this is your main() method. if you think about it, creating classes and connecting them to each other or wiring them is a special kind of logic: the logic is “building applications”. Is it better to spread this logic throughout the code or put it in one place to make it easier to maintain? The answer is that collecting it in one place is better - not only for maintenance, but also for this, all your significance classes turn into more useful and flexible components.

In your main() or the equivalent of main() you must create all the objects you need by passing them to each other's setters and constructors in order to "bind" them together. Then your unit tests will pass them differently, passing layouts or similar things. All this is done as an “dependency injection”. After I say, you will probably have a big ugly main() method. This is where a dependency injection tool can help you and actually make your code infinitely more flexible. A tool that I would suggest when you get to this point, like others, suggested another, Spring.

The less important rule # 2 is to always program on interfaces, it is still very important, since it eliminates all implementation dependencies, simplifies reuse, not to mention the use of other tools, such as mock object frameworks, ORM structures, etc. OK.

+2
source share

Even dependency injection frameworks like Spring, Guice, PicoContainer, etc., need some kind of extension, so you always need to create something.

I would suggest you use a / factory provider that returns a customized instance of the class. This will allow you to get out of the "creation" of the hierarchy.

+1
source share

Your constructors are not wrong, and the problem is not when / where the code is executed, but what everyone else mentioned: Injection Dependency. You need to create mock SshProperties objects to instantiate your object. The simplest way (if the class is not marked final) is to extend the class:

 public class MockSshProperties extends SshProperties { // implemented methods } 

You can use mock frameworks like Mockito :

 public class Info { private final sshProps; private final serviceProps; public Info() { this(new SshProperties(), new ServiceProperties()); } public Info(SshProperties arg1, ServiceProperties arg2) { this.sshProps = arg1; this.serviceProps = arg2 } } public class InfoTester { private final static SshProperties sshProps = mock(SshProperties.class); private final static ServiceProperties serviceProps = mock(ServiceProperties.class); static { when(sshProps.someGetMethod("something")).thenReturn("value"); } public static void main(String[] args) { Info info = new Info(sshProps, serviceProps); //do stuff } } 
+1
source share

The easiest answer is Spring. However, another answer is to put your configuration file in JNDI. Spring is in some ways the best answer, especially if you don't have anything that can change depending on the environment.

0
source share

You allow another class to have too much knowledge about the Info class and its dependencies. the dependency injection infrastructure will use the provider class. Using common types, you can create an Info Object Provider:

 interface Provider<T> { T get(); } 

If your other class takes a Provider <Info> in its constructor, your method will look like this:

 public void useInfo() throws Exception { Info info = infoProvider.get(); doStuffWithInfo(info); } 

This removed the construction of specific classes from your code. The next step is to make Info in the interface to simplify the creation of a layout for a specific unit test case.

And yes, it will push and propel the entire building code of the facility further and further. This will lead to some module that only describes how to combine things. The “rules” for such code are that it should be free of conditional statements and loops.

I recommend reading the Misko Heverys blog . All presentations are helpful and print out a guide to writing testable code as a small reference, once you understand the rules, that's good.

0
source share

All Articles