How do unit tests set data sources if they do not work on the application server?

Thank you all for your help. Some of you have sent (as I expected) answers indicating that my approach was wrong or that low-level code should never know if it works in the container. I would agree. However, I am dealing with a complex legacy application and am not able to do a lot of refactoring for the current problem.

Let me step back and ask a question motivated by my initial question.

I have an outdated application running under JBoss and made some changes to the lower level code. I created a unit test for my modification. To run the test, I need to connect to the database.

Inherited code gets the data source this way:

(jndiName is a specific string)

Context ctx = new InitialContext(); DataSource dataSource = (DataSource) ctx.lookup(jndiName); 

My problem is that when I run this code under unit test, the Context does not have specific data sources. My solution was to try to find out if I am running under the application server, and if not, create a test DataSource and return it. If I am running under the application server, I use the code above.

So my real question is: what is the right way to do this? Is there an approved way so that unit test can configure the context to return the appropriate data source so that the code under test does not need to know where it works?


For context: MY ORIGINAL QUESTION:

I have Java code that needs to know if it works under JBoss. Is there a canonical way for code to determine if it is running in a container?

My first approach was developed through experimentation and is to get the initial context and testing so that it can find specific values.

 private boolean isRunningUnderJBoss(Context ctx) { boolean runningUnderJBoss = false; try { // The following invokes a naming exception when not running under // JBoss. ctx.getNameInNamespace(); // The URL packages must contain the string "jboss". String urlPackages = (String) ctx.lookup("java.naming.factory.url.pkgs"); if ((urlPackages != null) && (urlPackages.toUpperCase().contains("JBOSS"))) { runningUnderJBoss = true; } } catch (Exception e) { // If we get there, we are not under JBoss runningUnderJBoss = false; } return runningUnderJBoss; } Context ctx = new InitialContext(); if (isRunningUnderJboss(ctx) { ......... 

Now it seems to work, but it looks like a hack. What is the โ€œrightโ€ way to do this? Ideally, I would like it to be with a lot of application servers, not just JBoss.

+6
java java-ee containers jboss detect
source share
7 answers

The whole approach seems wrong to me. If your application needs to know in which container it works, you are doing something wrong.

When I use Spring, I can move from Tomcat to WebLogic and vice versa without changing anything. I am sure that with the proper configuration, I could do the same trick with JBOSS. This is the goal I am shooting for.

+2
source share

The whole concept comes back to the fore. Lower level code should not conduct this kind of testing. If you need another implementation, skip it at the appropriate point.

+5
source share

Some combination of dependency injection (whether through Spring, configuration files, or program arguments) and the Factory pattern usually work best.

As an example, I pass an argument to my Ant scripts, which configure the configuration files depending on whether the ear or war goes into a development, testing or production environment.

+4
source share

Maybe something like this (ugly, but it might work)

  private void isRunningOn( String thatServerName ) { String uniqueClassName = getSpecialClassNameFor( thatServerName ); try { Class.forName( uniqueClassName ); } catch ( ClassNotFoudException cnfe ) { return false; } return true; } 

The getSpecialClassNameFor method returns a class that is unique to each application server (and can return new class names when adding more application servers)

Then you use it like:

  if( isRunningOn("JBoss")) { createJBossStrategy....etcetc } 
+1
source share

There are several ways to solve this problem. One of them is to pass the Context object to the class when it is in the unit test. If you cannot change the signature of the method, reorganize the creation of the inital context to a protected method and test a subclass that returns the deceived context object by overriding the method. This can at least put the class under test so you can reorganize the best alternatives from there.

The next option is to make connections to the factory database, which can determine whether it is in the container or not, and do the corresponding thing in each case.

One thing to think about - when you connect to this database from a container, what are you going to do with it? It is simpler, but it is not quite a unit test if you have to carry the entire level of data access.

For further assistance in this direction of moving legacy code to unit test, I suggest you take a look at Michael Feather working effectively with legacy code .

+1
source share
 Context ctx = new InitialContext(); DataSource dataSource = (DataSource) ctx.lookup(jndiName); 

Who creates the source code? Its design must be outside the code that you are trying to test, or else you will not be able to mock the context.

Since you said you were working on an outdated application, first reorganize the code so that you can easily depend on the context or data source. Then you can write tests for this class more easily.

You can translate legacy code with two constructors, as in the code below, until the code that creates the class is reorganized. This way you can check Foo more easily, and you can save the code that Foo uses unchanged. Then you can slowly reorganize the code so that the old constructor is completely removed and all the dependencies are introduced by the dependencies.

 public class Foo { private final DataSource dataSource; public Foo() { // production code calls this - no changes needed to callers Context ctx = new InitialContext(); this.dataSource = (DataSource) ctx.lookup(jndiName); } public Foo(DataSource dataSource) { // test code calls this this.dataSource = dataSource; } // methods that use dataSource } 

But before you start refactoring, you need to have some kind of integration tests to cover your back. Otherwise, you may not know whether even simple refactoring, such as moving a DataSource search into a constructor, can break something. Then, when the code gets better, more testable, you can write unit tests. (By definition, if the test is about a file system, network, or database, this is not a unit test - this is an integration test.)

The advantage of unit tests is that they run fast - hundreds or thousands per second - and are very focused on testing only one behavior at a time. This allows you to run often often (if you do not dare to run all unit tests after changing one line, they work too slowly) so that you get quick feedback. And since they are very focused, you will know, simply by looking at the name of the failed test, exactly where the error is in the production code.

The advantage of integration tests is that they ensure the correct connection of all parts. This is also important, but you cannot run them very often, because things like touching a database make them very slow. But you must run them at least once a day on the continuous integration server.

+1
source share

A clean way to do this would be to have lifecycle listeners set up in web.xml . They can set global flags if you wish. For example, you can define a ServletContextListener in web.xml and in the contextInitialized method, set the global flag, re works inside the container. If the global flag is not set, you are not working inside the container.

-one
source share

All Articles