I am using spring framework with selenium and testNG, then I decided to integrate with allure reports, the reports are working fine.
My issue is with attaching screenshots on failure.
The #Autowired driver is returning null when getScreenshotAs is called.
I'll post some code, if more is needed let me know.
#Component
public class ScreenshotUtil {
#Autowired
private WebDriver driver;
#Attachment(value = "Screenshot", type = "image/png")
public byte[] saveScreenshotOnFailure() {
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES); //this is where im getting error
}
}
Then I have the listener class..
public class AllureListener implements ITestListener {
#Autowired
private ScreenshotUtil screenshotUtil;
#Override
public void onTestFailure(ITestResult result) {
screenshotUtil.saveScreenshotOnFailure();
}
}
I have my test classes with #Test all working fine.. I'll add the WebDriverConfig component aswell (dont know if that will be useful).
#Configuration
public class WebDriverConfig {
#Bean
#Scope("browserscope") //This is for parallel run
public WebDriver driver() {
if(System.getProperty("browser").equals("firefox") {
.. return firefox driver //let me know if this code might be necessary
} else {
.. return chrome driver //let me know if this code might be necessary
}
}
#Bean
public WebDriverWait webDriverWait(WebDriver driver) {
return new WebDriverWait(driver, timeout);
}
}
Thanks in advance, any help is appreciated..
See if the #Component annotation will solve your issue, since you're using #Autowired on a property.
Decorate the driver() method in WebDriverConfig with #Component("driver"); and see if it works.
More details here - https://www.baeldung.com/spring-autowire
It would be better if you could put the entire stack trace.
It appears that the configuration class is not able to provide the driver bean.
You can try the following two things.
Try removing the #Scope (which would make the bean singleton that shouldn't be an issue here, you can use "prototype" scope )
Debug the code place debug point inside the Bean and see if the bean is getting returned.
I want to answer my own question, because after a lot of help and some research I was able to solve this issue.
What happens is, spring context is different from testNG context.
When we implement ITestListener class, the TestNG context does not have access to spring context. This class is necessary for taking screenshots on test failure.
So the solution is (I took it from here: https://dzone.com/articles/autowiring-spring-beans-into-classes-not-managed-by-spring)
First create this service to retrieve the beans from spring:
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then on my AllureListener class I'll retrieve the ScreenshotUtil bean like this:
public class AllureListener implements ITestListener {
#Override
public void onTestFailure(ITestResult result) {
ScreenshotUtil screenshotUtil = BeanUtil.getBean(ScreenshotUtil.class);
screenshotUtil.saveScreenshotOnFailure();
}
}
Hopefully this will help someone in the future using spring framework, spring and allure report.
Related
I'm trying to develop my first java Spring Bboot app that calls Strava API and gets my activities for the given period of time.
I've registered my app on Strava's website and got client_id and client secret.
I've generated spring-swagger-codegen-api-client and awtowired the client to the app.
#Configuration
public class StravaIntegrationConfiguration {
#Bean
public ActivitiesApi stravaApi(){
return new ActivitiesApi(apiClient());
}
#Bean
public ApiClient apiClient(){
return new ApiClient();
}
}
Then I use this bean in AdapterClass
public class Adapter {
#Autowired
private static ActivitiesApi activitiesApi;
public static void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
System.out.println("ВСЕГО АКТИВНОСТЕЙ"+ loggedInAthleteActivities.size());
}
}
#SpringBootApplication
#Import(StravaIntegrationConfiguration.class)
public class App {
public static void main(String[] args) throws SQLException {
SpringApplication.run(App.class);
Adapter.getActivities(1636130496, 1635529296, 1, 30);
}
}
When I run this code I get NPE, because activitiesApi is null.
What is the problem? Please kindly advise.
Does it concern authentication? Could you advise also any code sample on how to make Strava authentication in my app?
It has nothing to do with Strava authentication. It is related to Spring context and Spring Beans and how to inject them. As already mentioned you can't autowire Spring-managed beans in static fields (it makes no sense actually). Having said that you need to fix that first:
#Component
public class Adapter {
#Autowired
private ActivitiesApi activitiesApi;
public void getActivities(Integer before, Integer after, Integer page, Integer perPage) {
final List<SummaryActivity> loggedInAthleteActivities = activitiesApi.getLoggedInAthleteActivities(before, after, page, perPage);
System.out.println("ВСЕГО АКТИВНОСТЕЙ"+ loggedInAthleteActivities.size());
}
}
Also, note that the method changed from a static one to an instance one and that the annotation #Component was added to the class. The reason is that a Spring-managed bean can only be injected into other Spring-managed beans.
Additionally, it seems to me that you are trying to do something after the Spring context has been initialized. One possible way to do this is creating a bean that implements the ApplicationListener interface:
#Component
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private Adapter adapter;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
adapter.getActivities(1636130496, 1635529296, 1, 30);
}
}
This means that you can and you should remove the line Adapter.getActivities(1636130496, 1635529296, 1, 30); from your main class:
#SpringBootApplication
#Import(StravaIntegrationConfiguration.class)
public class App {
public static void main(String[] args) throws SQLException {
SpringApplication.run(App.class);
}
}
Finally, and as a side note, please consider using constructor injection instead of field injection. It has a couple of advantages over field injection: making the class easier to unit test, allowing the objects to be immutable, explicitly definition of which dependencies are mandatory, etc... (you can read more at https://reflectoring.io/constructor-injection/).
I've spent several days looking for a way to move one of my #BeforeClass methods to listener class I can reference in xml where I define content os test suite.
Problem I'm facing is that I'm using Spring for DI, and in #BeforeClass method I add some attributes to testng context, so I can use them in other places (other listeners).
I tried using onStart(final ITestContext context) from ITestListener. But that method seems to be invoked before spring manages to create beans, and I cannot perform my operations, because all my beans are nulls.
I tried using onBeforeClass(ITestClass testClass) from IClassListener. But that method only provides ITestClass, which does not give me access to context, so I can't set my attributes.
Now I'm experimenting with onConfigurationSuccess(final ITestResult itr) from IConfigurationListener, but that requires using if statement to run my code only if configuration method name is equal to springTestContextPrepareTestInstance.
Does anyone know a better way of doing this?
[EDIT] code sample
#Component
public class CleanupHelper {
private static SomeBean someBean;
#Autowired
public CleanupHelper(SomeBean someBean){
CleanupHelper.someBean = someBean;
}
public static Object getSomething(){
return someBean.getSomething();
}
}
public class ExcludedGroupsListener implements IConfigurationListener {
#Override
public void onConfigurationSuccess(final ITestResult itr) {
if (itr.getName().contains("springTestContextPrepareTestInstance")) {
var something = CleanupHelper.getSomething();
if (something != null && someOtherCondition) {
itr.setAttribute("someObject", something);
}
}
}
}
#ContextConfiguration(classes = TestConfig.class)
public class SomeTests extends AbstractTestNGSpringContextTests {
#Test
public void someTest(){
// doSomething
}
}
#Configuration
#ComponentScan(basePackages = "com.some",
excludeFilters = #Filter(type = FilterType.REGEX, pattern = "com.some.else..*"))
public class TestConfig {
}
Above code works... unfortunately onConfigurationSuccess method is invoked after each configuration method.
Try with Annotation Transformers.
You can add it in your testng.xml like any other listener.
And in there you can do things like:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
public class TestAnnotationTransformer implements IAnnotationTransformer {
#SuppressWarnings("rawtypes")
#Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
if (testMethod.getName().equals("MyTest1"))
annotation.setGroups( new String[] {"GroupA" });
if(ignoreTestDependencies)
annotation.setIgnoreMissingDependencies(true);
}
}
Just an example, but you have many things there to play with.
Just bear in mind that, as I stated in the comments, this runs before runtime, so you won't be able to change things on the go like you would do with a normal listener.
I am trying to test my Spring configuration class which is annotated with ConditionalOnCloudPlatform.
Here is a very simplified example of the configuration class (I can't post my actual code):
#Configuration
#ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudConfigurationExample {
#Bean
public MyBean myBean(MyProperties properties) {
return new MyBean(properties.getParam);
}
}
To test I was hoping to do this:
#RunWith(MockitoJUnitRunner.class)
public class CloudConfigurationExampleTest {
private CloudConfigurationExample cloudConfigurationExample;
private MyProperties myProperties;
#Before
public void setUp() {
myProperties = new MyProperies();
myProperties.setParam("test");
cloudConfigurationExample = new CloudConfigurationExample(myProperties);
}
#Test
public void test() {
MyBean myBean = cloudConfigurationExample.myBean();
// do asserts etc.
}
}
The issue I have is that ConditionalOnCloudPlatform is activated and expects a valid cloud connector to be present. As a result I get No suitable cloud connector found.
Does anyone know the correct way so get Junit to ignore this annotation? I tried setting an environment variable with VCAP_SERVICES, which is what this annotation expects, but it didn't work.
Thanks!
ConditionalOnCloudPlatform gets activated if environment contains properties VCAP_APPLICATION and VCAP_SERVICES
There are different ways to overcome this issue,
Firstly, ensure no properties containing above prefixes are passed.
Secondly, check for cloud profile #Profile("cloud") or ignore this class during test #Profile("!test") and many more ways.
code snippet:
CLOUD_FOUNDRY {
#Override
public boolean isActive(Environment environment) {
return environment.containsProperty("VCAP_APPLICATION")
|| environment.containsProperty("VCAP_SERVICES");
}
},
Currently I have a factory class that looks like this:
#ContextConfiguration(classes = BeanConfig.class)
public class FactoryClass extends AbstractTestNGSpringContextTests {
#Autowired
public Bean bean;
#Factory(dataProvider="dataProvider")
public Object[] createTest(int a, int b) {
return new Object[]{new FactoryTestClass(a, b)};
}
#DataProvider(name="dataProvider",parallel=true)
public Object[][] passInts(){
bean.method();
return new Object[][]{{2,2},{2,3},{2,4},{2,4}};
}
#BeforeSuite
public void beforeSuite(){
System.out.println("before suite");
}
}
My goal is to use spring's autowiring feature so i can use a bean to help generate some test data for the data provider. However in my attempt the spring context never initialises. Does anyone know what I might be doing wrong, or is there another approach I can take?
Thank you kindly,
Jason
I had some similar issue: my test folder was located outside directory main, so, after I marked it as the Test Source Resource (in Intellij IDE) it started to work. Hope it helps.
Try to add loader=AnnotationConfigContextLoader.class to ContextConfiguration.
I would suggest to locate #DataProvider is same class as #Test method. I never had a problem with this approach.
Having various #Test methods and various dataProviders in one test class is valid usage. #Test method will specify which dataProvider is used in #Test annotation parameter.
Example:
#DataProvider(name="dataProvider",parallel=true)
public Object[][] passInts(){
bean.method();
return new Object[][]{{2,2},{2,3},{2,4},{2,4}};
}
#Test(dataProvier="dataProvider")
public test(int param1, int param2){
//...
}
I am still a beginner in Spring . I have one test class as below an run by TestNG ...
#Service("springTest")
public class SpringTest {
private MyService myService;
#Autowired
public void setMyService(MyService myService) {
this.myService = myService;
// below was ok , nothing error
System.out.println(myService.getClass());
}
//org.testng.annotations.Test
#Test
public void doSomethingTest() {
load();
// That may cause NullPointerException
myService.doSomething();
}
private void load(){
// here codes for load spring configuration files
}
}
So , I have no idea why myService.doSomething(); produce NullPointerException ? Can someone give me suggestions what I need to do ? What problem may cause this error ? What am I wrong ? Now I am using as ...
#Service("springTest")
public class SpringTest {
private static MyService myService;
#Autowired
public void setMyService(MyService myService) {
SpringTest.myService = myService;
System.out.println(myService.getClass());
}
//org.testng.annotations.Test
#Test
public void doSomethingTest() {
load();
// fine , without error
myService.doSomething();
}
private void load(){
// here codes for load spring configuration files
}
}
PS: I think I don't need to show my spring configuration file , because it is really simple and I believe that will not be cause any error. So , I left it to describe. But if you want to see , I can show you. And then please assume my spring configurartion file was loaded properly and this was loaded before my Test classes run.
Thanks for reading my question.By the way, I can't also use field
injection and I can only use setter method injection.
Just remove the static initializer. Keep setter injection (I prefer it).
Use your first example, the second code is wrong - don't use it.
Make sure you tests are being run by spring (and therefore the bean is initialized correctly by spring).
The null pointer is caused by running the test method before spring has initialized the bean.
You need somethign like this
#RunWith (SpringJUnit4ClassRunner.class)
#ContextConfiguration (locations = "classpath:/config/applicationContext-test.xml")
public class SpringTest {...}