How to use spring autowiring in a testng factory class - java

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){
//...
}

Related

Reading custom annotation for JUNIT

I have a custom annotation which I use as config to start off one time set-up for Junit.
#Target(TYPE) #Retention(RUNTIME)
public #interface MyAnnotation{
String host();
int port();
}
Test class:
#MyAnnotation(host="0.0.0.0", port=4567)
public class MyTest extends MyAbstractClass{
#Test
public void myTest(){
//do testy things
}
}
Superclass:
public class MyAbstractClass{
#BeforeAll
public static void start(){
Config cfg = readConfig();
//Use config to do one time set-up
}
private static Config readConfig(){
MyAnnotation ann = MyTest.class.getAnnotation(MyAnnotation.class);
return new Config(ann.host(), ann.port());
}
}
So currently, I hardcode the name of the test class (MyTest) in readConfig(..).
This won't work when I add a second test class.
One way to solve it is:
Add another #BeforeAll method in MyTest which will call the #BeforeAll in super-class and pass the class name as a param.
However, I am curious if I can read the name of the executing subclass in the superclass via some reflexion magic.
Any ideas are most welcome.
Thanks
The presence of the #BeforeAll annotation suggests you are using JUnit 5. In this case, you can use.
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInfo;
public class MyAbstractClass {
#BeforeAll
public static void start(TestInfo ti) {
Config cfg=readConfig(ti.getTestClass().orElseThrow(IllegalStateException::new));
//Use config to do one time set-up
}
private static Config readConfig(Class<?> testClass) {
MyAnnotation ann = testClass.getAnnotation(MyAnnotation.class);
return new Config(ann.host(), ann.port());
}
}
See also the TestInfo API documentation.
This is not “Reflection Magic” but a feature provided by JUnit itself, but it’s also only JUnit which knows that the invocation of a static method annotated with #BeforeAll is associated with a particular test class it is going to process.

getScreenshotAs is giving null exception on webDriver

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.

Unable to mock object of class residing in different project

This is how I am mocking object of class Client present in another project:
#Mock
private Client client;
//Mocking method of class client -
#Test
public void test()
{
Mockito.when(client.getPassportDetail(Matchers.eq(bytes),Matchers.eq(properties)))
.thenReturn(hash);
}
Structure of class Client:
class Client
{
public static boolean loadLibraries(Properties properties) {
}
public HashMap<String, String> getPassportDetail(byte[] b, Properties properties) throws Exception{
if (!loadLibraries(properties))
{
throw new UnsatisfiedLinkError();
}
}
So, when I mock getPassportDetail method, it gets called, not mocked.
That's a common mistake. Actually #Mock annotation need something more to work as You want to.
You have 3 options there:
Add
#RunWith(MockitoJUnitRunner.class)
to Your test class.
Or
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
There is also 3rd option, that #Dawood ibn Kareem suggest in comment below:
There's a third option, which is better than either of these two. Use the new Mockito Rule - #Rule public MockitoRule rule = MockitoJUnit.rule();. More detail at my answer here, which also explains why this is the best of the three options.
That should be all.
You can always refer to http://www.baeldung.com/mockito-annotations for more informations and detailed explanation.

How to autowire field in static #BeforeClass?

#RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
#Autowired
private EntityRepository dao;
#BeforeClass
public static void init() {
dao.save(initialEntity); //not possible as field is not static
}
}
How can I have my service injected already in the static init class?
With Junit 5 you can do this (#BeforeAll instead of #BeforeClass)
public void ITest {
#Autowired
private EntityRepository dao;
#BeforeAll
public static void init(#Autowired EntityRepository dao) {
dao.save(initialEntity); //possible now as autowired function parameter is used
}
}
By leaving the field it means it can be used in other tests
One workaround that I have been using to get this working is to use #Before with a flag to skip it being executed for each testcase
#RunWith(SpringJUnit4ClassRunner.class)
public class BaseTest {
#Autowired
private Service1 service1;
#Autowired
private Service2 service2;
private static boolean dataLoaded = false;
#Before
public void setUp() throws Exception {
if (!dataLoaded) {
service1.something();
service2.somethingElse();
dataLoaded = true;
}
}
}
UPD for Spring 2.x versions.
Spring 2.x supports new feature a SpringExtension for Junit 5 Jupiter, where all you have to do is:
Declare your test class with #ExtendWith(SpringExtension.class)
Inject your #BeforeAll (replacement for #BeforeClass in JUnit 5) with the bean
For example:
#ExtendWith(SpringExtension.class)
...
public void ITest {
#BeforeAll
public static void init(#Autowired EntityRepository dao) {
dao.save(initialEntity);
}
}
Assuming you correctly configured JUnit 5 Jupiter with Spring 2.x
More about it here: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-junit-jupiter-extension
It looks to me that you are trying to populate DB before tests.
I would give a try to two options:
If you can extract initial scripts to sql file (if that is option for you without using repository bean) you can use this approach and annotate your test with #Sql
You can explore DbUnit and here is link to spring dbunit connector which is doing exactly that and helping you populate DB before tests. Here is a github link for integrating between spring test framework and dbunit. After you do that you have #DatabaseSetup and #DatabaseTearDown which will do thing on DB you need
I know that this does not answer how to inject bean in static #BeforeClass but form code it looks it is solving your problem.
Update:
I recently run into same problem in my project and dug out this article which helped me and I think it is elegant way of dealing with this type of problem. You can extend SpringJUnit4ClassRunner with listener which can do instance level setup with all your defined beans.
To answer this question we should recap Spring 2.x versions.
If you want to "autowire" a bean in your #BeforeTest class you can use the ApplicationContext interface. Let's see an example:
#BeforeClass
public static void init() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
EntityRepository dao2 = (EntityRepository) context.getBean("dao");
List<EntityRepository> all = dao2.getAll();
Assert.assertNotNull(all);
}
What's happening: using the ClassPathXmlApplicationContext we are instantiating all beans contained in the application-context.xml file.
With context.getBean() we read the bean specified (it must match the name of the bean!); and then you can use it for your initialization.
You should give to the bean another name (that's the dao2!) otherwise Spring normal "autowired" cannot work on the predefined bean.
As a side note, if your test extends AbstractTransactionalJUnit4SpringContextTests you can do some initialization using executeSqlScript(sqlResourcePath, continueOnError); method, so you don't depend on a class/method that you also have to test separately.
If you just want to use some DB data in your tests, you could also mock the repository and use the #Before workaround Narain Mittal describes:
#RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
#MockBean
private EntityRepository dao;
#Before
public static void init() {
when(dao.save(any())).thenReturn(initialEntity);
}
}

How to disable #PostConstruct in Spring during Test

Within a Spring Component I have a #PostConstruct statement. Similar to below:
#Singleton
#Component("filelist")
public class FileListService extends BaseService {
private List listOfFiles = new Arrays.list();
//some other functions
#PostConstruct
public void populate () {
for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new String[]{"txt"},true)){
listOfFiles.add(f.getName());
}
}
#Override
public long count() throws DataSourceException {
return listOfFiles.size();
}
// more methods .....
}
During Unit tests I would not like to have the #PostConstruct function called, is there a way to telling Spring not to do post processing? Or is there a better Annotation for calling a initiation method on a class durning non-testing ?
Any of:
Subclass FileListService in your test and override the method to do nothing (as mrembisz says, you would need to place the subclass in package scanned only for tests and mark it as #Primary)
Change FileListService so the list of files is injected by Spring (this is a cleaner design anyway), and in your tests, inject an empty list
Just create it with new FileListService() and inject the dependencies yourself
Boot up Spring using a different configuration file/class, without using annotation configuration.
Since you are not testing FileListService but a depending class, you can mock it for tests. Make a mock version in a separate test package which is scanned only by test context. Mark it with #Primary annotation so it takes precedence over production version.
Declare a bean to override the existing class and make it Primary.
#Bean
#Primary
public FileListService fileListService() {
return mock(FileListService.class);
}
check the profile as this:
#PostConstruct
public void populate () {
if (!Arrays.asList(this.environment.getActiveProfiles()).contains("test")) {
for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new
String[]{"txt"},true)){
listOfFiles.add(f.getName());
}
}
}

Categories