使用 Mockito、TestNG 和 OpenEJB 对 EJB 进行单元测试
Unit testing an EJB with Mockito, TestNG and OpenEJB
我有以下 EJB:
PersonService.java
@Local
public interface PersonService {
long countPersons();
}
PersonServiceImpl.java
@Stateless
public class PersonServiceImpl implements PersonService {
@EJB
private RemotePersonService remotePersonService;
@Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
远程PersonService.java
@Local
public interface RemotePersonService {
List<Person> getAllPersons();
}
RemotePersonServiceImpl.Java
@Stateless
public class RemotePersonServiceImpl {
@Override
public List<Person> getAllPersons() {
// Here, I normally call a remote webservice, but this is for the purpose of this question
List<Person> results = new ArrayList<Person>();
results.add(new Person("John"));
return results;
}
}
这是我的测试
AbstractTest.java
public abstract class AbstractTest {
private InitialContext context;
@BeforeClass(alwaysRun = true)
public void setUp() throws Exception {
System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));
context = new InitialContext(properties);
context.bind("inject", this);
}
@AfterClass(alwaysRun = true)
public void tearDown() throws Exception {
if (context != null) {
context.close();
}
}
}
PersonServiceTest.java
@LocalClient
public class PersonServiceTest extends AbstractTest {
@EJB
private PersonService personService;
@Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 1l);
}
}
现在,我想要做的是用 Mockito 的模拟替换 PersonServiceImpl.java 中的 RemotePersonService 实现,并且在我的 [=43] 中仍然有相同的调用=]testPersonService 方法。
我试过了:
PersonServiceTest.java
@LocalClient
public class PersonServiceTest extends AbstractTest {
@Mock
private RemotePersonService remotePersonService;
@EJB
@InjectMocks
private PersonService personService;
@BeforeMethod(alwaysRun = true)
public void setUpMocks() {
MockitoAnnotations.initMocks(this);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
}
@Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
但这行不通。 PersonService中没有注入@Mock RemotePersonService,依然使用真正的EJB
我怎样才能使这个工作?
不要在测试中使用注释。有一个构造函数可以连接所有依赖项。
@Stateless
public class PersonServiceImpl implements PersonService {
@EJB
private RemotePersonService remotePersonService;
// Let your test instantiate a mock service and wire it into your test instance using this constructor.
public PersonServiceImpl(RemotePersonService rps) {
this.remotePersonService = rps;
}
@Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
创建模拟并将它们传递给它。您的测试可能如下所示:
@LocalClient
public class PersonServiceTest extends AbstractTest {
@Test
public void testPersonService() {
RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
PersonService personService = new PersonServiceImpl(mockRemotePersonService);
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
我在 class 上使用 setters,并查找 ejb
private ServicioAsyncMessaging servicioNotificaciones;
我删除了@EJB --> 和 getter
public ServicioAsyncMessaging getServicioNotificaciones() {
if(servicioNotificaciones == null){
servicioNotificaciones = (ServicioAsyncMessaging)Lookup.getEjb(EjbJndiConstantes.EJB_SERVICIO_ASYNC_MSG);
}
return servicioNotificaciones;
}
public void setServicioNotificaciones(ServicioAsyncMessaging servicioNotificaciones) {
this.servicioNotificaciones = servicioNotificaciones;
}
查找 es:
public static Object getEjb(String lookupName){
Object t = null;
try {
Context ctx = new InitialContext();
t= ctx.lookup(lookupName);
} catch (NamingException e) {
log.error("getEjb | Error {}",e.getMessage(),e);
}
return t;
}
通过这些更改 mockito --> 在 setter 上注入模拟。
我有以下 EJB:
PersonService.java
@Local
public interface PersonService {
long countPersons();
}
PersonServiceImpl.java
@Stateless
public class PersonServiceImpl implements PersonService {
@EJB
private RemotePersonService remotePersonService;
@Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
远程PersonService.java
@Local
public interface RemotePersonService {
List<Person> getAllPersons();
}
RemotePersonServiceImpl.Java
@Stateless
public class RemotePersonServiceImpl {
@Override
public List<Person> getAllPersons() {
// Here, I normally call a remote webservice, but this is for the purpose of this question
List<Person> results = new ArrayList<Person>();
results.add(new Person("John"));
return results;
}
}
这是我的测试
AbstractTest.java
public abstract class AbstractTest {
private InitialContext context;
@BeforeClass(alwaysRun = true)
public void setUp() throws Exception {
System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));
context = new InitialContext(properties);
context.bind("inject", this);
}
@AfterClass(alwaysRun = true)
public void tearDown() throws Exception {
if (context != null) {
context.close();
}
}
}
PersonServiceTest.java
@LocalClient
public class PersonServiceTest extends AbstractTest {
@EJB
private PersonService personService;
@Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 1l);
}
}
现在,我想要做的是用 Mockito 的模拟替换 PersonServiceImpl.java 中的 RemotePersonService 实现,并且在我的 [=43] 中仍然有相同的调用=]testPersonService 方法。
我试过了:
PersonServiceTest.java
@LocalClient
public class PersonServiceTest extends AbstractTest {
@Mock
private RemotePersonService remotePersonService;
@EJB
@InjectMocks
private PersonService personService;
@BeforeMethod(alwaysRun = true)
public void setUpMocks() {
MockitoAnnotations.initMocks(this);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
}
@Test
public void testPersonService() {
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
但这行不通。 PersonService中没有注入@Mock RemotePersonService,依然使用真正的EJB
我怎样才能使这个工作?
不要在测试中使用注释。有一个构造函数可以连接所有依赖项。
@Stateless
public class PersonServiceImpl implements PersonService {
@EJB
private RemotePersonService remotePersonService;
// Let your test instantiate a mock service and wire it into your test instance using this constructor.
public PersonServiceImpl(RemotePersonService rps) {
this.remotePersonService = rps;
}
@Override
public long countPersons() {
return remotePersonService.getAllPersons().size();
}
}
创建模拟并将它们传递给它。您的测试可能如下所示:
@LocalClient
public class PersonServiceTest extends AbstractTest {
@Test
public void testPersonService() {
RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
List<Person> customResults = new ArrayList<Person>();
customResults.add(new Person("Alice"));
customResults.add(new Person("Bob"));
Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
PersonService personService = new PersonServiceImpl(mockRemotePersonService);
long count = personService.countPersons();
Assert.assertEquals(count, 2l);
}
}
我在 class 上使用 setters,并查找 ejb
private ServicioAsyncMessaging servicioNotificaciones;
我删除了@EJB --> 和 getter
public ServicioAsyncMessaging getServicioNotificaciones() {
if(servicioNotificaciones == null){
servicioNotificaciones = (ServicioAsyncMessaging)Lookup.getEjb(EjbJndiConstantes.EJB_SERVICIO_ASYNC_MSG);
}
return servicioNotificaciones;
}
public void setServicioNotificaciones(ServicioAsyncMessaging servicioNotificaciones) {
this.servicioNotificaciones = servicioNotificaciones;
}
查找 es:
public static Object getEjb(String lookupName){
Object t = null;
try {
Context ctx = new InitialContext();
t= ctx.lookup(lookupName);
} catch (NamingException e) {
log.error("getEjb | Error {}",e.getMessage(),e);
}
return t;
}
通过这些更改 mockito --> 在 setter 上注入模拟。