Creo que en inglés me habría quedado un título más correcto. Algo así como: 'Mocking static methods for testing in Maven 2 using Surrogate'.
El problema
El caso es que me encuentro con que la clase que quiero testear llama al método estático OpenCms.getRoleManager(). Además, el proyecto se construye con Maven 2 (ó 3), por lo que habrá que modificar el POM consiguientemente.
Esta es la clase:
package arm.opencms.accesscontrol;
import org.opencms.file.CmsObject;
import org.opencms.main.OpenCms;
import org.opencms.security.CmsRole;
public class XmlContentAccessControl {
private CmsRole ACCEPTED_ROLE=CmsRole.VFS_MANAGER;
public boolean hasPermission(CmsObject cmsObject) {
return OpenCms.getRoleManager.hasRole(cmsObject,ROLE);
}
}
La solución más cabal: refactorizar
En lugar de adaptar nuestro código de prueba, podemos adaptar nuestro código principal para que sea más testeable. En este caso podemos declarar un atributo de la clase para almacenar un CmsRoleManager:
package arm.opencms.accesscontrol;
import org.opencms.file.CmsObject;
import org.opencms.security.CmsRoleManager;
import org.opencms.security.CmsRole;
public class XmlContentAccessControl2 {
private CmsRoleManager roleManager;
public XmlContentAccessControl(CmsRoleManager roleManager){
this.roleManager=roleManager;
}
private CmsRole ACCEPTED_ROLE=CmsRole.VFS_MANAGER;
public boolean hasPermission(CmsObject cmsObject) {
return this.roleManager.hasRole(cmsObject,ROLE);
}
}
Si refactorizar no es posible o deseable, una posible solución: Surrogate
Si la única salida es llamar al método estático, podemos usar el framework
Surrogate, que, apoyándose en
AspectJ, permite definir sustituciones de los métodos o de las clases principales en nuestro código de prueba. Una de sus funcionalidades es la de sobreescribir métodos estáticos.
Para nuestro ejemplo, usaremos el siguiente árbol de directorios con sus correspondientes contenidos:
/
|-src
| |-main
| | |-java
| | | |-arm/opencms/accesscontrol
| | | |-XmlContentAccessControl.java
| | |-aspect
| | |-OpenCmsStaticCalls.aj
| |-test
| |-java
| -arm/opencms/accesscontrol
| |-TestAccessControl.java
|-test
| |-lib
| |-surrogate-core-1.0rc1.jar
|-pom.xml
Y el contenido de los ficheros:
pom.xml
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>arm.opencms</groupid>
<artifactid>arm.opencms.accesscontrol</artifactid>
<name>AccessControl</name>
<dependencies>
<dependency>
<groupid>org.opencms</groupid>
<artifactid>opencms</artifactid>
<version>7.5.2</version>
</dependency>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.jmock</groupid>
<artifactid>jmock-junit4</artifactid>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>net.sourceforge.surrogate</groupid>
<artifactid>surrogate-core</artifactid>
<version>1.0rc1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.aspectj</groupid>
<artifactid>aspectjtools</artifactid>
<version>1.6.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.aspectj</groupid>
<artifactid>aspectjrt</artifactid>
<version>1.6.7</version>
</dependency>
<dependency>
<groupid>mockobjects</groupid>
<artifactid>mockobjects-core</artifactid>
<version>0.09</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactid>maven-install-plugin</artifactid>
<executions>
<execution>
<id>install-javabridge-javabridge</id>
<phase>initialize</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>test/lib/surrogate-core-1.0rc1.jar</file>
<groupid>net.sourceforge.surrogate</groupid>
<artifactid>surrogate-core</artifactid>
<packaging>jar</packaging>
<version>1.0rc1</version>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupid>org.codehaus.mojo</groupid>
<artifactid>aspectj-maven-plugin</artifactid>
<version>1.3</version>
<configuration>
<compliancelevel>1.5</compliancelevel>
<aspectdirectory>src/test/aspect</aspectdirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
La biblioteca Surrogate no está disponible en ningún repositorio público, por lo que la instalamos durante la inicialización como vimos en
un post anterior. También se usa el
plugin de AspectJ, para compilar el aspecto OpenCmsStaticCalls.aj .
TestAccessControl.java
@RunWith(JMock.class)
public class XmlContentAccessControlTest {
private Mockery context = new JUnit4Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
CmsObject mockCmsObject=context.mock(CmsObject.class);
CmsRoleManager mockRoleManager=context.mock(CmsRoleManager.class);
@Test
public void testHasPermissionOnTab()throws NoSuchMethodException, CmsRoleViolationException {
SurrogateManager mm = SurrogateManager.getInstance();
mm.reset();
//OpenCms.getRoleManager() -> mockRoleManager
MockMethod mockGetRoleManager = mm.addMockMethod(new MockMethod(OpenCms.class, "getRoleManager"));
mockGetRoleManager.addReturnValue(mockRoleManager);
context.checking(new Expectations() {{
allowing(mockRoleManager).hasRole(with(is(mockCmsObject)),
(CmsRole)with(allOf(an(CmsRole.class),hasProperty("roleName", is("VFS_MANAGER")))));
will(returnValue(true));
}});
XmlContentAccessControl accessControl=new XmlContentAccessControl();
assertTrue(accessControl.hasPermission(mockCmsObject));
}
OpenCmsStaticCalls.aj
aspect OpenCmsStaticCalls extends net.sf.surrogate.core.SurrogateCalls {
/**
* Implementation of the mock pointcut
*/
protected pointcut mockPointcut() :
(
call(* org.opencms.main.OpenCms.*(..))
);
}