Is it possible to log spock feature method names and clause labels? - spock

I'd like to be able to log the spock feature names and clause labels when running some automated tests. This would help with debugging test issues when using a headless browser for automation, specifically phantomjs. Reason being, phantomjs does not always behave the same way as when using the chrome WebDriver. It would also be nice to have if this is even possible.
def "Login logout test"(){
given: "Go to login page"
...
when: "Submit username and password"
...
then: "Dashboard page displayed"
...
when: "logout"
...
then: "Returned to login page"
...
}
For example, It would be cool if I could get the above sample spock feature method to log the labels like this.
Login logout test
Go to login page
Submit username and password
logout
Returned to login page

Step1: Create a Your own spock extension Class
package com.example.spock.exetension;
public class MySpockExtension implements IGlobalExtension {
#Override
public void start() {
}
#Override
public void visitSpec(SpecInfo spec) {
spec.addListener(new MyCustomSpockRunListener());
}
#Override
public void stop() {
}
}
Step2: Create a RunListener that can listen to a spock run
package com.example.spock.exetension;
public class MyCustomSpockRunListener extends AbstractRunListener {
private boolean specFailed;
private boolean featureFailed;
#Override
public void beforeSpec(SpecInfo spec) {
// TODO Auto-generated method stub
specFailed = false;
}
#Override
public void beforeFeature(FeatureInfo feature) {
// TODO Auto-generated method stub
featureFailed = false;
}
#Override
public void beforeIteration(IterationInfo iteration) {
}
#Override
public void afterIteration(IterationInfo iteration) {
}
#Override
public void afterFeature(FeatureInfo feature) {
// TODO Auto-generated method stub
for ( BlockInfo block : feature.getBlocks() ) {
System.out.println(block.getKind().name() + " : " + block.getTexts() );
}
}
#Override
public void afterSpec(SpecInfo spec) {
// TODO Auto-generated method stub
System.out.println(spec.getName() + " : STATUS : " + specFailed != null ? "failure":"success");
}
#Override
public void error(ErrorInfo error) {
specFailed = true;
FeatureInfo feature = error.getMethod().getFeature();
if (feature != null) {
featureFailed = true;
System.out.println(error.getMethod().getName() + " : " + error.getException());
}else {
}
}
#Override
public void specSkipped(SpecInfo spec) {
}
#Override
public void featureSkipped(FeatureInfo feature) {
}
}
Step3: Register your new Spock extension
In your classpath or resource path create a below folder structure META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
Have this as the content of file com.example.spock.exetension.MySpockExtension
Step4: Run your spock test and you should see output something like this.
given: "Go to login page"
when: "Submit username and password"
then: "Dashboard page displayed"
when: "logout"
then: "Returned to login page"
Login logout test : STATUS : success

You can get the name of every feature method by following :
import spock.lang.Specification
import org.junit.Rule
import org.junit.rules.TestName
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class MySpec extends Specification{
private static Logger logger = LoggerFactory.getLogger(ClassName.class)
#Rule TestName testName = new TestName()
void setup(){
def featureMethodName = testName.methodName
logger.info("feature method : " + featureMethodName)
}
}

PiggyBacking on #Raghu Kirans answer, I had to do a little bit more to get this to run the way that I wanted with Data Driven tests. In the BeforeIteration method of your RunListener I did the following:
#Override
public void beforeIteration(IterationInfo iteration) {
Optional.of(iteration)
.map(feature -> iteration.getFeature())
.map(FeatureInfo::getBlocks)
.ifPresent( blocks -> blocks.forEach(
blockInfo -> log.info(blockInfo.getKind().name() + " : " + blockInfo.getTexts())));
}
This simply prints out everything prior to each iteration. Also note that getKind().name() on the BlockInfo object does not print out the given, when, then of the spock block in our test but instead prints out SETUP, WHEN, THEN and WHERE instead. getTexts() will print out the combined texts of the block.
Example:
given: "I wake up"
and: "I drink a cup of coffee"
Will be displayed as
SETUP : ["I wake up", "I drink a cup of coffee"]

After continuously searching I found this solution for getting the test name. But can't seem to find anything on the 'when' and 'then' labels. This is okay for now.
import org.junit.Rule
import org.junit.rules.TestName
class MySpec extends Specification {
#Rule TestName name = new TestName()
def "some test"() {
expect: name.methodName == "some test"
}
}

You might want to have a look at the Spock Reports Extension

Related

Micronaut #Replaces with declarative Client

I am going to use the code from Micronaut Documentation (Declarative Http Client)- And I'm using Spock
PetOperations.java
#Validated
public interface PetOperations {
#Post
Single<Pet> save(#NotBlank String name, #Min(1L) int age);
}
I have a declarative client:
#Client("/pets")
public interface PetClient extends PetOperations {
#Override
Single<Pet> save(String name, int age);
}
My goal is when I run a test class, I want to call (#Replaces) another class (PetDummy) instead of the PetClient, PetDummy class is located in my test folder
#Primary
#Replaces(PetClient.class)
#Singleton
public class PetDummy implements PetOperations {
#Override
public Single<Pet> save(String name, int age) {
Pet pet = new Pet();
pet.setName(name);
pet.setAge(age);
// save to database or something
return Single.just(pet);
}
}
test class:
class PetTest extends Specification {
#Shared
#AutoCleanup
ApplicationContext applicationContext = ApplicationContext.run();
//EmbeddedServer server = applicationContext.getBean(EmbeddedServer.class).start();
PetClient client = applicationContext.getBean(PetOperations.class);
def 'test' (){
given: 'name and age'
when:
client.save("Hoppie", 1);
then:
noExceptionThrown()
}
}
However, at the end PetClient is called, I have as well tried with the #Factory annotation, but no success
PetClient extends PetOperations and PetDummy implements PetOperations, if they both implement then it will make sense to use #Replaces ...
Is there something else I can try out?
Thank you!
Another Issue:
Now that it works, the PetClient is a dependency in my PetService. When I test my PetService, it still calls the PetClient instead of the PetDummy.
I assume it has to do with the applicationContext, you will see
PetService:
PetService {
#Inject
PetClient client;
buyFood(){
//...
Single<Pet> pet = client.save("Hoppie", 1));
}
}
PerService Test:
class PetServiceTest extends ApplicationContextSpecification {
#Subject
#Shared
PetService petService = applicationContext.getBean(PetService)
PetOperations client = applicationContext.getBean(PetOperations.class) //client is not used here
def 'test' (){
given:
when:
petService.buyFood()
then:
noExceptionThrown()
}
}
I think that I need to "get into" the applicationContext from the PetService, to tell "use the PetDummy" implementation (Inside the test class, because the ApplicationContextSpecification belong to another module
The ApplicationContextSpecification is:
abstract class ApplicationContextSpecification extends Specification implements ConfigurationFixture {
#AutoCleanup
#Shared
ApplicationContext applicationContext = ApplicationContext.run(configuration)
/* def cleanup() {
assert !hasLeakage()
}*/
}
The ConfigurationFixture contains the properties for the database(hibernate)
You are already retrieving the PetClient bean implementation:
PetClient client = applicationContext.getBean(PetOperations.class);
Which should provide the replacing dummy bean implementation if called with the appropriate type:
PetOperations client = applicationContext.getBean(PetOperations.class);

Getting "Too few invocations" on unit test with spock

For simplicity let's take a very simple class:
public class TestingClass {
public void method1(){
System.out.println("Running method 1");
method2();
}
public void method2(){
System.out.println("Running method 2");
}
}
Now I'm writing a simple test, which checking that when we invoke method1(), method2() is invoked:
class TestingClassSpec extends Specification {
void "method2() is invoked by method1()"() {
given:
def tesingClass = new TestingClass()
when:
tesingClass.method1()
then:
1 * tesingClass.method2()
}
}
by executing this test, I'm getting the following error:
Running method 1 Running method 2
Too few invocations for:
1 * tesingClass.method2() (0 invocations)
Why I'm getting this error? Printed log is show that method2() was invoked.
You need to use Spy when testing interactions on real objects, see below:
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class TestingClassSpec extends Specification {
void "method2() is invoked by method1()"() {
given:
TestingClass tesingClass = Spy()
when:
tesingClass.method1()
then:
1 * tesingClass.method2()
}
}
public class TestingClass {
public void method1(){
System.out.println("Running method 1");
method2();
}
public void method2(){
System.out.println("Running method 2");
}
}

Grails service ->java.lang.NullPointerException: Cannot invoke method serviceMethod() on null object

I have Class in src/groovy . I want to use my service here . but error occurred "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here ". i try to debug but not able to find . can you please help me that what is my mistake .
class ListenerSession implements HttpSessionListener {
def transactionService = new TransactionService ()
public ListenerSession() {
}
public void sessionCreated(HttpSessionEvent sessionEvent){
}
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
User user=session["user"]
if(user){
try{
java.util.Date date = session['loginDate']
transactionService.updateUserLastLogin(user,date)
-----}catch (Exception e) {
println e
}
code in service is:
def updateUserLastLogin(User user,Date date){
try{
User.withTransaction{
println "121212"
user.lastLogin=date
user.loginDuration=new Date().time - user?.lastLogin?.time
def x=user.save()
}
}catch (Exception e) {
println e
}
}
Don't instantiate services with new. If they use nearly any piece of Grails framework, that piece won't work - like GORM session in this case.
Here's an exactly your question: http://grails.1312388.n4.nabble.com/Injecting-Grails-service-into-HttpSessionListener-can-it-be-done-td1379074.html
with Burt's answer:
ApplicationContext ctx = (ApplicationContext)ServletContextHolder.
getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
transactionService = (TransactionService) ctx.getBean("transactionService")
Grails won't inject your service for you in the src/groovy level and just declaring a new instance of TransactionService will not give you all the goodies (hence your error). You need to get your instance form the spring context like so...
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class ListenerSession implements HttpSessionListener {
public ListenerSession() {
}
public void sessionCreated(HttpSessionEvent sessionEvent){
}
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
User user=session["user"]
if(user){
try{
java.util.Date date = session['loginDate']
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def transactionService = ctx.transactionService
transactionService.updateUserLastLogin(user,date)
}catch (Exception e) {
println e
}
}
}
}

How to get name of currently running test in spock?

In JUnit 3, I could get the name of the currently running test like this:
public class MyTest extends TestCase {
public void testSomething() {
assertThat(getName(), is("testSomething"));
}
}
How do I do this in spock? I would like to use the test name as a key in a shared resource so that tests don't interfere with each other.
One solution is to leverage JUnit's TestName rule:
import org.junit.Rule
import org.junit.rules.TestName
class MySpec extends Specification {
#Rule TestName name = new TestName()
def "some test"() {
expect: name.methodName == "some test"
}
}
This requires JUnit 4.7 or higher.
For spock 1.0-groovy-2.4 you can try :
def "Simple test"() {
expect:
specificationContext.currentIteration.name == "Simple test"
}

Guice 3.0 #ScopeSession User object not invalidate on session timeout

I have a class :
#SessionScoped
public class LoggedUser {
private User user;
...
}
that I use to keep track if a user is logged in my application.
In my Struts2 application I have a Interceptor to check if the user is logged, if not he's forwarded to the login page.
public class LoggedUserInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 2822434409188961460L;
#Inject
private LoggedUser loggedUser;
#Override
public String intercept(ActionInvocation invocation) throws Exception {
if(loggedUser==null || !loggedUser.isLogged()){
return "login";
}
return invocation.invoke();
}
}
The problem occur when the session timeout. The object LoggdeUser is never null or deleted. I have always the last instance.
I added A session listener.
public class SessionListener implements HttpSessionListener {
private static Logger logger = Logger.getLogger(SessionListener.class.getName());
#Override
public void sessionCreated(HttpSessionEvent event) {
logger.info("sessionCreated = " + event.getSession().getId());
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
logger.info("sessionDestroyed = " + event.getSession().getId());
}
}
I see that sessionDestroyed is called, but when I enter again in my Interceptor.. LoggedUser is never recreated for a new session.
why ?
my Struts2 Action for the login is this
public class LoginUserAction extends ActionSupport implements ModelDriven<LoggedUser>, ServletRequestAware {
...
#Inject
private LoggedUser loggedUser;
public String execute() throws Exception {
...
loggerUser.setLoggedTime(now);
...
return SUCCESS;
}
I add that too in web.xml
session-config
session-timeout 2 /session-timeout
/session-config
I don't know anything about Struts2, but my guess is that the interceptor's scope is wider than session scope. In other words, the interceptor instance is kept around longer than the session. Guice can't and won't set an injected field to null when the session ends, nor will it ever re-inject an object automatically.
What you need to do if you use an object with a shorter lifecycle inside an object with a longer lifecycle (such as a RequestScoped object inside a SessionScoped object or a SessionScoped object inside a singleton) is inject a Provider for the shorter lived object.
In your case, I think this is probably what you need:
public class LoggedUserInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 2822434409188961460L;
#Inject
private Provider<LoggedUser> loggedUserProvider;
#Override
public String intercept(ActionInvocation invocation) throws Exception {
LoggedUser loggedUser = loggedUserProvider.get();
if(loggedUser==null || !loggedUser.isLogged()){
return "login";
}
return invocation.invoke();
}
}
I don't know guice but the session is available to the interceptor and can be made readily available to the action via SessionAware :
ActionInvocation provides access to the session. The following is part of a "Authentication" interceptor. If there is not a "User" object then Action.LOGIN is returned.
public String intercept(ActionInvocation invocation) throws Exception {
Map session = invocation.getInvocationContext().getSession();
appLayer.User user = (appLayer.User) session.get("User");
if (user == null){
return Action.LOGIN;
}
return invocation.invoke();
}
I only place the user object on the session when the user logs in.
In the login action I place the user object on the session:
public class Login extends ActionSupport implements SessionAware {
private Map<String, Object> session;
private String userName;
private String password;
public String execute() {
//use DB authentication once this works
//if ("ken".equalsIgnoreCase(userName) && "ken".equalsIgnoreCase(password)){
try {
User user = new User(userName, password);
session.put("User", user);
return ActionSupport.SUCCESS;
} catch (Exception e) {
System.out.println("There was an exception" + e.getMessage());
return ActionSupport.LOGIN;
}
}
public void validate() {
String user = this.getUserName();
String pass = this.getPassword();
//This block is a bit redundant but I couldn't figure out how
//to get just the hibernate part to report an exception on Username/pass
if (!StringUtils.isBlank(this.getUserName()) && !StringUtils.isBlank(this.getPassword())) {
try {
Class.forName("com.ibm.as400.access.AS400JDBCDriver").newInstance();
String url = "jdbc:as400://192.168.1.14";
DriverManager.getConnection(url, user, pass).close();
} catch (Exception e) {
addFieldError("login.error.authenticate", "Bad Username / Password");
}
}
if (StringUtils.isBlank(getUserName())) {
addFieldError("login.error.name", "Missing User Name");
}
if (StringUtils.isBlank(getPassword())) {
addFieldError("login.error.password", "Missing Password");
}
//else both are blank don't report an error at this time
}
... getters/setters....
}//end class
If I remember correctly this comes from at least in part from "Struts2 in Action". Anyways I'm a big believer in DI but since the Session is pretty accessible from the interceptors and the action I don't bother.
the easiest way to do that is finally to put the token in the session at the login and check it with an Interceptor
#Override
public String intercept(ActionInvocation invocation) throws Exception {
loggedUser = (LoggedUser) invocation.getInvocationContext().getSession().get(LoggedUser.SESSIONTOKEN);
if(loggedUser==null || !loggedUser.isLogged()){
logger.info("loggedUser not present in session");
return "login";
}
return invocation.invoke();
}
in the Action
request.getSession().setAttribute(LoggedUser.SESSIONTOKEN, loggedUser);

Resources