First, let's say one conclusion: unit tests are isolated from the spring container of the main project, that is, unit tests cannot access the main Project Spring container and need to load the spring container yourself.
Next is the code instance, where the Web master project is running, and you might see code like this in the unit test:
Code One: current Class-Loaded
public class Testspring {
@Test public
void Testspring () {
Loginservice loginservice = This.getbean (" Loginservice ");
}
The following is a container instance declaration and initialization, destruction of
private classpathxmlapplicationcontext context;
@Before public
Void before () {
//Load Spring container
context = new Classpathxmlapplicationcontext (" Spring-context.xml ");
}
@After public
Void after () {
Context.destroy ();
}
Gets the bean from the static variable ApplicationContext and automatically transforms it into the type of the assigned value object.
Public <T> T Getbean (String name) {
return (T) Context.getbean (name);
}
Gets the bean from the static variable ApplicationContext and automatically transforms it into the type of the assigned value object.
Public <T> T Getbean (class<t> requiredtype) {
return Context.getbean (requiredtype);
}
}
Code two: Inherit the loaded type
/**
* @Description: Login unit Test Class
*/Public
class Logintest extends springjunitsupport{
@Autowired
Private Loginservice Loginservice;
@Test public
void Testlogin () {
loginservice.login ();
}
}
Let unit tests run in the spring environment, with spring framework-related support
@RunWith (springjunit4classrunner.class)
//Load Spring container
@ Contextconfiguration ("Classpath:/spring-context.xml") public
class Springjunitsupport {
}
Code three: Dynamically add Spring profile
/** * @Description: Login unit Test class */public class logintest{//use @before annotations to load Spring container configuration file, cannot pass
The bean is injected in a way that is automatically assembled, since the automatic assembly annotations are performed earlier than @before//@Autowired private loginservice loginservice;
Private Testspringcontextsupport springcontextsupport = null;
@Before public void SetUp () throws Exception {springcontextsupport = new testspringcontextsupport ();
When the spring container is initialized, the spring bean configuration file springcontextsupport.init (new string[] {"Classpath:/support-quartz.xml"} is added dynamically;
Loginservice = Springcontextsupport.getbean ("Loginservice");
} @Test public void Testlogin () {loginservice.login (); }
}
public class Testspringcontextsupport {//Initializes a static variable with a static statement block to hold the Spring container profile public static list<string> cont
Extlist = new arraylist<string> ();
static {Contextlist.add ("classpath:/spring-context.xml");
} private ApplicationContext context; Define initialization method, dynamically add spring configuration file to static profile collection public void init (string[] contextfile) {list<string> List = new Array
List<string> ();
List.addall (contextlist);
for (int i = 0; Contextfile! = null && i < contextfile.length; i++) {List.add (contextfile[i]);
} string[] x = new string[list.size ()];
List.toarray (x);
Load Spring Container context = new Classpathxmlapplicationcontext (x);
}//Gets the bean from the static variable ApplicationContext and automatically transforms to the type of the assigned value object.
Public <T> T Getbean (String name) {return (T) Context.getbean (name);
}//Gets the bean from the static variable ApplicationContext and automatically transforms to the type of the assigned value object. Public <T> T Getbean (class<t&Gt
Requiredtype) {return Context.getbean (requiredtype); }
}
As in the above three ways, there is one thing in common, that is, before the unit test method executes, it is a lot of trouble to load the spring container.
Because the Web master project is running, why does the unit test also load the spring container separately, because the spring container for the Web master project is isolated from the unit test and verified by the following means:
Verification 1:
Remove all the code that loads the spring container from the unit test to ensure that the main project is running, with @autowired annotations (@Autowired annotations can assemble spring internal beans), and get the beans for the spring application context. Then get the business bean through it, the code is as follows:
/**
* @Description: Login unit Test Class
*
/public class
logintest{//Auto Assemble Spring application context Bean
@Autowired
Private ApplicationContext context;
@Test public
void Testlogin () {
//By applying a context bean, get business bean
loginservice loginservice = (loginservice) Context.getbean ("Loginservice");
Loginservice.login ();
}
}
The result must be a null pointer exception, and the context object is null.
verify two, stop the web main project, and unit test load the spring container using the second inherited method above, as in the following code:
/**
* @Description: Login unit test class, Inherit Springjunitsupport load Spring container
*/Public
class Logintest extends springjunitsupport{
//Auto Assemble Spring application context Bean
@Autowired
private applicationcontext context;
@Test public
void Testlogin () {
//By applying a context bean, get business bean
loginservice loginservice = (loginservice) Context.getbean ("Loginservice");
Loginservice.login ();
}
}
@RunWith (Springjunit4classrunner.class)
//Load Spring container
@ContextConfiguration ("classpath:/ Spring-context.xml ") Public
class Springjunitsupport {
}
As a result, this verifies that the unit tests are isolated from the spring container of the main project and that the unit test must load the spring container itself.
It's been said that loading the spring container is actually loading the configuration file, loading the bean inside the config file into the spring container, and verifying that it is important to understand and apply this by searching the Bean object in the Spring container.
the last of the eggs, understanding is because the project is confused, explored before you can understand thoroughly, such as an example:
1, the main project runs, provides the service interface, adopts the way for the Dubbo+zookeeper way;
2, Unit test, call the service provided by the provider, using the inherited load spring configuration file;
3. Throw exception: The address has been bound to use (addr already in use:bind)
java.lang.IllegalStateException:Failed to load ApplicationContext ...
caused By:com.alibaba.dubbo.rpc.RpcException:Fail to start server (url:dubbo://127.0.0.1:20880/...
......
caused by:com.alibaba.dubbo.remoting.RemotingException:Failed to bind Nettyserver on/127.0.0.1:20880, cause:failed to Bind to:/0.0.0.0:20880 ...
caused by:org.jboss.netty.channel.ChannelException:Failed to bind to:/0.0.0.0:20880 ...
caused by:java.net.BindException:Address already in Use:bind
...
4. Exception reason: Because the Dubbo+zookeeper method is used, the main project Spring provider registers the 127.0.0.1:20880, the unit test loads the spring configuration file to register the 0.0.0.0:20880 address, However, 20880 is already occupied by the primary container, so unit tests do not load properly.
5, the solution: to stop the main container, using unit testing alone, that is, as a service side and as a client
6. Throw the exception again:
DEBUG [2016-08-18 18:30:26,603]-Zkclient.java ()-Closing zkclient ...
INFO [2016-08-18 18:30:26,603]-Zkeventthread.java ()-Terminate zkclient event thread. DEBUG [2016-08-18 18:30:26,603]-Zkconnection.java ()-Closing ZooKeeper connected to 119.254.166 .167:2181 DEBUG [2016-08-18 18:30:26,603]-Zookeeper.java ()-Closing session:0x15678a538f900 EF DEBUG [2016-08-18 18:30:26,603]-Clientcnxn.java ()-Closing Client for session:0x15678a538
F900ef ... DEBUG [2016-08-18 18:30:26,808]-Zkclient.java ()-Closing zkclient...done INFO [2016-08-18 18:30:26,810]-Dubboprotocol.java ()-[Dubbo] Close Dubbo Server:/127.0.0.1:20880, Dubbo version : 2.5.4, current host:127.0.0.1 INFO [2016-08-18 18:30:26,812]-Abstractserver.java ()-[Dubbo] Close Nettyserver bind/0.0.0.0:20880, export/127.0.0.1:20880, Dubbo version:2.5.4, current host:127.0.0.1 INFO [2016-08-18 18:30:26,812]-Clientcnxn.java ()-Eventthread shut down for Session:0x15678a538f900ef ERROR [2016-08-18 18:30:26,813]-failback Registry.java ()-[Dubbo] Failed to Uregister dubbo://127.0.0.1:20880/... com.alibaba.dubbo.rpc.Rp cexception:failed to unregister dubbo://127.0.0.1:20880/business method ... at Com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doUnregister (zookeeperregistry.java:108) at Com.alibaba.dubbo.registry.support.FailbackRegistry.unregister (failbackregistry.java:160) at Com.alibaba.dubbo.registry.integration.registryprotocol$1.unexport (registryprotocol.java:130) at Com.alibaba.dubbo.config.ServiceConfig.unexport (serviceconfig.java:270) at Com.alibaba.dubbo.config.spring.ServiceBean.destroy (servicebean.java:255) at Org.springframework.beans.factory.support.DisposableBeanAdapter.destroy (Disposablebeanadapter.java: 258) at Org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean ( defaultsingletonbeanregistry.java:538) at Org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton ( defaultsingletonbeanregistry.java:514) at Org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton ( defaultlistablebeanfactory.java:831) at Org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons ( defaultsingletonbeanregistry.java:483) at Org.springframework.context.support.AbstractApplicationContext.destroyBeans (Abstractapplicationcontext.java : 923) at Org.springframework.context.support.AbstractApplicationContext.doClose (Abstractapplicationcontext.java : 897) at Org.springframework.context.support.abstractapplicationcontext$1.run (Abstractapplicationcontext.java : 811) caused by:java.lang.NullPointerException at org. I0itec.zkclient.zkclient$8.call (zkclient.java:720) at Org. i0iTec.zkclient.ZkClient.retryUntilConnected (zkclient.java:675) at Org. I0Itec.zkclient.ZkClient.delete (zkclient.java:716) at
Com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient.delete (zkclientzookeeperclient.java:57) At Com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doUnregister (zookeeperregistry.java:106) ... More
The key information that needs to be noted above can be summed up as: Zookeeper client shutdown (Closing zkclient ...), Dubbo service shutdown (Close Dubbo server ...), logoff Dubbo a method failed (Failed to Uregister dubbo://127.0.0.1:20880/...)
Although not carefully explore what is going on, but it should be the unit test load both the server and the client (that is, load the spring configuration file), when the test method is completed to close the service, due to sequencing problems caused by the exception.
7, the solution: The provider is initiated by the main container, as for the unit test, on the last egg that sentence, unit testing as a client, only need to get the service provider's Bean object, you can complete the service end of the provider's call.
So where does this object come from, the Dubbo+zookeeper method configures the subscription service's configuration file in the client, which has the provider's corresponding bean, so the unit test only needs to load the client subscription configuration file, the code is as follows:
@RunWith (Springjunit4classrunner.class)
@ContextConfiguration ({"Classpath:/spring/test-dubbo-consumer.xml") }) public
class Coderulerpcimpldubbotest {
@Autowired
private userrpc userrpc;
@Test public
void Testgetuserbycode () {
Bizcode = Userrpc.getuserbycode ("001");
}
}
Client subscriber configuration file:
<?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "Http://www.springframework.org/schema/beans" xmlns:
Xsi= "Http://www.w3.org/2001/XMLSchema-instance" xmlns:context= "Http://www.springframework.org/schema/context"
Xmlns:jdbc= "Http://www.springframework.org/schema/jdbc" xmlns:jee= "Http://www.springframework.org/schema/jee"
xmlns:tx= "Http://www.springframework.org/schema/tx" xmlns:util= "Http://www.springframework.org/schema/util"
xmlns:task= "Http://www.springframework.org/schema/task" xmlns:dubbo= "Http://code.alibabatech.com/schema/dubbo" xsi:schemalocation= "Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/ Spring-beans-4.0.xsd Http://www.springframework.org/schema/context Http://www.springframework.org/schema/context /spring-context-4.0.xsd Http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/ Spring-jdbc-4.0.xsd Http://www.springframework.org/schema/jee httP://www.springframework.org/schema/jee/spring-jee-4.0.xsd HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/TX/HTTP Www.springframework.org/schema/tx/spring-tx-4.0.xsd Http://www.springframework.org/schema/util/HTTP Www.springframework.org/schema/util/spring-util-4.0.xsd Http://www.springframework.org/schema/task/HTTP Www.springframework.org/schema/task/spring-task-4.0.xsd Http://code.alibabatech.com/schema/dubbo/HTTP Code.alibabatech.com/schema/dubbo/dubbo.xsd "> <!--Consumer app name information, this is equivalent to a name, we Dubbo Management page is more clear which application is exposed--&L T;dubbo:application name= "Spring_dubbo_consumer" ></dubbo:application> <!--using zookeeper registry to expose the service address-- > <dubbo:registry address= "zookeeper://127.0.0.1:2181" check= "true" ></dubbo:registry> < !--User Management Service to reference-<dubbo:reference interface= "Com.test.rpc.UserRPC" id= "Userrpc" ></dubbo:referenc E> </beans>
The above problem is resolved, and the relationship between the spring container, unit test, Spring-bean, and the Dubbo and zookeeper are also deeply understood.