I was trying to achieve, Cucumber feature level parallel execution using pico Container.
When I am using a shared Driver in a context Class as below, I get org.picocontainer.PicoCompositionException: Duplicate Keys not allowed. Duplicate
public class Context{
private ThreadLocal<WebDriver> drivers = new ThreadLocal<>();
public void setDriver(WebDriver wd) {
drivers.set(wd);
}
public WebDriver getDriver() {
return drivers.get();
}
//Runner Class
import java.net.MalformedURLException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import cucumber.api.CucumberOptions;
import cucumber.api.testng.CucumberFeatureWrapper;
import cucumber.api.testng.TestNGCucumberRunner;
import net.thumbtack.cucumber.picocontainer.example.step.SharedDriver;
import cucumber.api.testng.*;
#CucumberOptions (glue = {"net.thumbtack.cucumber.picocontainer.example.step"},
features = "src/main/resources/"
,tags = {"#Scenario2,#Scenario3"})
public class TestRunner {
public TestRunner() throws MalformedURLException {
super();
// TODO Auto-generated constructor stub
}
private TestNGCucumberRunner testNGCucumberRunner;
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
System.setProperty("ExecEnv","Docker");
}
// #Test(dataProvider = "features")
// public void feature(PickleEventWrapper eventwrapper,CucumberFeatureWrapper cucumberFeature) throws Throwable {
#Test(groups="cucumber", description="Runs CucumberFeature",dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature){
testNGCucumberRunner.runCucumber(cucumberFeature.getCucumberFeature());
// testNGCucumberRunner.runScenario(eventwrapper.getPickleEvent());
}
#DataProvider(parallel=true)
public Object[][] features() {
return testNGCucumberRunner.provideFeatures();
// return testNGCucumberRunner.provideScenarios();
}
#AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
Related
I am trying to mock my controller and write testcases for it. But when I tried to debug the Test class the control is not getting into my Controller class. I am not sure what I am doing wrong here.
Please help me to resolve this as I am stuck on this for almost more that 3 hours.
My Service class
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bnpp.leavemanagement.dao.DepartmentRepository;
import com.bnpp.leavemanagement.model.DepartmentModel;
#Service
public class DepartmentService
{
#Autowired
DepartmentRepository depRepo;
public List<DepartmentModel> listDepartment = new ArrayList<>();
public List<DepartmentModel> getAllDepartments()
{
List<DepartmentModel> allDepartment = depRepo.findAll();
return allDepartment;
}
public DepartmentModel fetchDepartmentById( int id)
{
DepartmentModel depDetail = null;
try
{
depDetail = depRepo.findById(Long.valueOf(id)).get();
}
catch(Exception ex)
{
depDetail = null;
}
return depDetail;
}
public DepartmentModel addDepartment(DepartmentModel depDetail)
{
DepartmentModel depExists = null;
if (listDepartment.size() > 0)
{
depExists = listDepartment.stream()
.filter(d -> d.getName().equalsIgnoreCase(depDetail.getName()))
.findFirst()
.orElse(null);
}
//Below condition is to restrict duplicate department creation
if(depExists == null)
{
depRepo.save(depDetail);
listDepartment.add(depDetail);
}
else
{
return null;
}
return depDetail;
}
public DepartmentModel updateDepartment(DepartmentModel depDetail)
{
DepartmentModel depUpdate = null;
try
{
depUpdate = depRepo.findById(depDetail.getId()).get();
depUpdate.setName(depDetail.getName());
depRepo.save(depUpdate);
}
catch(Exception ex)
{
depUpdate = null;
}
return depUpdate;
}
public DepartmentModel deleteDepartment(DepartmentModel depDetail)
{
try
{
depRepo.deleteById(depDetail.getId());
}
catch(Exception ex)
{
return null;
}
return depDetail;
}
}
My Test Class
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Description;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.web.context.WebApplicationContext;
import com.bnpp.leavemanagement.controller.DepartmentController;
import com.bnpp.leavemanagement.model.DepartmentModel;
import com.bnpp.leavemanagement.service.DepartmentService;
#ExtendWith(MockitoExtension.class)
#WebMvcTest(DepartmentController.class)
#ContextConfiguration(classes = com.bnpp.leavemanagementsystem.LeaveManagementSystemApplicationTests.class)
public class DepartmentControllerTest {
#MockBean
DepartmentService depService;
#Autowired
private MockMvc mockMvc;
#InjectMocks
DepartmentController departmentController;
#Autowired
private WebApplicationContext webApplicationContext;
#Test
#Description("Should return a list of DepartmentModel objects when called")
void shouldReturnListOfDepartmentModel() throws Exception
{
DepartmentModel depModel = new DepartmentModel();
depModel.setId(1L);
depModel.setName("Mock");
List<DepartmentModel> listDepmodel = new ArrayList<>();
listDepmodel.add(depModel);
Mockito.when(depService.getAllDepartments()).thenReturn(listDepmodel);
mockMvc.perform(MockMvcRequestBuilders.get("/department"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.size()", Matchers.is(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(1))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("Mock"));
}
}
My Controller class
package com.bnpp.leavemanagement.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.bnpp.leavemanagement.dao.DepartmentRepository;
import com.bnpp.leavemanagement.model.DepartmentModel;
import com.bnpp.leavemanagement.service.DepartmentService;
#RestController
public class DepartmentController
{
#Autowired
DepartmentService depService;
#GetMapping("/department")
public ResponseEntity<List<DepartmentModel>> getDepartments()
{
List<DepartmentModel> allDepartment = depService.getAllDepartments();
if(allDepartment.size() == 0)
{
return new ResponseEntity( HttpStatus.NOT_FOUND);
}
return new ResponseEntity( allDepartment, HttpStatus.OK);
}
#GetMapping("/department/{id}")
public ResponseEntity<DepartmentModel> fetchDepartmentById(#PathVariable("id") int id)
{
DepartmentModel resDep = depService.fetchDepartmentById(id);
if(resDep == null)
{
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
else
{
return new ResponseEntity( resDep, HttpStatus.OK);
}
}
#PostMapping("/department/create")
public ResponseEntity<DepartmentModel> createDepartment(#RequestBody DepartmentModel depNew)
{
DepartmentModel resDep = depService.addDepartment(depNew);
if(resDep == null)
{
return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
}
else
{
return new ResponseEntity( resDep, HttpStatus.CREATED);
}
}
#PutMapping("/department/update")
public ResponseEntity<DepartmentModel> updateDepartment(#RequestBody DepartmentModel depNew)
{
DepartmentModel resDep = depService.updateDepartment(depNew);
if(resDep == null)
{
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
else
{
return new ResponseEntity( resDep, HttpStatus.OK);
}
}
#DeleteMapping("/department/delete")
public ResponseEntity<DepartmentModel> deleteDepartment(#RequestBody DepartmentModel depDel)
{
DepartmentModel resDep = depService.deleteDepartment(depDel);
if(resDep == null)
{
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
else
{
return new ResponseEntity(HttpStatus.OK);
}
}
}
The minimal viable test setup to use MockMvc with #WebMvcTest is the following:
#WebMvcTest(DepartmentController.class)
class DepartmentControllerTest {
#MockBean
DepartmentService depService;
#Autowired
private MockMvc mockMvc;
#Test
#Description("Should return a list of DepartmentModel objects when called")
void shouldReturnListOfDepartmentModel() throws Exception {
DepartmentModel depModel = new DepartmentModel();
depModel.setId(1L);
depModel.setName("Mock");
List<DepartmentModel> listDepmodel = new ArrayList<>();
listDepmodel.add(depModel);
Mockito.when(depService.getAllDepartments()).thenReturn(listDepmodel);
mockMvc.perform(MockMvcRequestBuilders.get("/department"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.size()", Matchers.is(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(1))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].name").value("Mock"));
}
}
Explanation:
#InjectMocks is not necessary as with #MockBean you're adding a mocked version of DepartmentService to your Spring TestContext and Spring DI's mechanism will inject it to your DepartmentController
#ExtendWith(SpringExtension.class) is also redundant, as the meta annotation #WebMvcTest already activates the SpringExtension.class
You don't need #ContextConfiguration here as #WebMvcTest takes care to detect your Spring Boot entrypoint class and start a sliced context
If the test still doesn't work, please add all your dependencies, the way you structure your code, and your main Spring Boot class (annotated with #SpringBootApplication).
Further reads that might shed some light on this:
Difference Between #Mock and #MockBean
Guide to Testing Spring Boot Applications With MockMvc
I am using cucumber BDD, testng, java to write some BDD test. I would like to mock static classes in order to write my test. However when I write this testrunner, it fails to initialize the BDD scenarios.
Complete Example(note the commented line PrepareForTest) :
import gherkin.events.PickleEvent;
import io.cucumber.testng.CucumberOptions;
import io.cucumber.testng.PickleEventWrapper;
import io.cucumber.testng.TestNGCucumberRunner;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
#CucumberOptions(
features = {
"src/test/resources/features/sample"
},
glue = {
"com.demo.stepdefinitions.sample"
},
plugin = {
"pretty",
"html:target/cucumber-reports/cucumber-pretty",
"json:target/cucumber-reports/sampple-report.json",
"rerun:target/cucumber-reports/sample-rerun.txt"
}
)
//#PrepareForTest({Util.class})
public class TestngWithDataproviderTest extends PowerMockTestCase {
private TestNGCucumberRunner testNGCucumberRunner;
private void mockActiveBucket() {
PowerMockito.mockStatic(Util.class);
PowerMockito.when(Util.getBucketId(any(Long.class))).thenReturn(3);
}
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
#Test(dataProvider = "users")
public void testMockStatic(String username){
System.out.println("username: " + username);
System.out.println("static test passed");
mockActiveBucket();
Assert.assertTrue(true);
}
#Test(groups = "cucumber scenarios", description = "Runs Cucumber Scenarios", dataProvider = "scenarios")
public void testCucumberCcenario(PickleEventWrapper pickleEvent) throws Throwable {
PickleEvent event = pickleEvent.getPickleEvent();
mockActiveBucket();
testNGCucumberRunner.runScenario(pickleEvent.getPickleEvent());
Assert.assertTrue(true);
}
#DataProvider(name = "scenarios")
public Object[][] scenarios() {
Object[][] scenarios = testNGCucumberRunner.provideScenarios();
return new Object[][]{{scenarios[0][0]}};
}
#DataProvider(name = "users")
public Object[][] users() {
return new Object[][]{{"user1"}, {"user2"}};
}
}
class Util {
public static int getBucketId(long eventTimestamp){
Long minsPast5MinBoundary = (eventTimestamp % TimeUnit.MINUTES.toMillis(5))/TimeUnit.MINUTES.toMillis(1);
return minsPast5MinBoundary.intValue();
}
}
The above test fails to load BDD scenarios dataProvider if I enable PrepareForTest annotation on the test. However, the other test which uses dataProvider works fine in both cases(enable or disable PrepareForTest)
ERROR:
Data provider mismatch
Method: testCucumberCcenario([Parameter{index=0, type=io.cucumber.testng.PickleEventWrapper, declaredAnnotations=[]}])
Arguments: [(io.cucumber.testng.PickleEventWrapperImpl) "Sunday isn't Friday"]
at org.testng.internal.reflect.DataProviderMethodMatcher.getConformingArguments(DataProviderMethodMatcher.java:45)
at org.testng.internal.Parameters.injectParameters(Parameters.java:796)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:983)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
As a side effect of this, I am unable to mock static methods of util class while writing the BDD. I am new to cucumber BDD. Any help/pointers is appreciated.
After getting some help to root cause from #help-cucumber-jvm slack channel, I was able to root cause it to testng+powermock with dataproviders using custom classes.
For example this test fails
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.ObjectFactory;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
#PrepareForTest(Util2.class)
public class TestngWithDataproviderTestngTest extends PowerMockTestCase {
#ObjectFactory
public org.testng.IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
private void mockActiveBucket() {
PowerMockito.mockStatic(Util.class);
PowerMockito.when(Util.getBucketId(any(Long.class))).thenReturn(3);
}
#Test(dataProvider = "users")
public void testMockStatic(MyTestCaseImpl myTestCase) {
System.out.println("myTestCase: " + myTestCase);
System.out.println("static test passed");
mockActiveBucket();
Assert.assertTrue(true);
}
#DataProvider(name = "users")
public Object[][] users() {
return new Object[][]{{new MyTestCaseImpl(5)}};
}
}
//interface MyTestCase {
//}
class MyTestCaseImpl { //implements MyTestCase{
int i;
public MyTestCaseImpl() {
}
public MyTestCaseImpl(int i) {
this.i = i;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
class Util2 {
public static int getBucketId(long eventTimestamp) {
Long minsPast5MinBoundary = (eventTimestamp % TimeUnit.MINUTES.toMillis(5)) / TimeUnit.MINUTES.toMillis(1);
return minsPast5MinBoundary.intValue();
}
}
Here as mentioned, seems to be a known issue with a workaround. Hope this helps.
How to generate extent report for cucumber + testng framework in such a way that on each scenario failure I can get the screen shot captured, without repeating the code with every scenario in step definition file
I have setup the Testing framework using Cucumber+Testng. However, I need extent reporting but not sure how to achieve it through testNG runner class without actually repeating the code with every scenario of step definition. So the idea is to write code in one place just like using cucumber hooks which will run for each and every scenario.
I Have already tried the approach with TestNG listener with Extent report but with this the drawback is I have to write the code every time for each and every scenario. LIke below I have ITestListnerImpl.java, ExtentReportListner and YouTubeChannelValidationStepDef where for each scenario I have to repeat the loginfo and screencapture methods
Code: ItestListerner.java
package com.testuatomation.Listeners;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import com.aventstack.extentreports.ExtentReports;
public class ITestListenerImpl extends ExtentReportListener implements ITestListener {
private static ExtentReports extent;
#Override
public void onFinish(ITestContext arg0) {
// TODO Auto-generated method stub
extent.flush();
System.out.println("Execution completed on UAT env ......");
}
#Override
public void onStart(ITestContext arg0) {
// TODO Auto-generated method stub
extent = setUp();
System.out.println("Execution started on UAT env ......");
}
#Override
public void onTestFailedButWithinSuccessPercentage(ITestResult arg0){
// TODO Auto-generated method stub
}
#Override
public void onTestFailure(ITestResult arg0) {
// TODO Auto-generated method stub
System.out.println("FAILURE");
}
#Override
public void onTestSkipped(ITestResult arg0) {
System.out.println("SKIP");
}
#Override
public void onTestStart(ITestResult arg0) {
System.out.println("STARTED");
}
#Override
public void onTestSuccess(ITestResult arg0) {
// TODO Auto-generated method stub
System.out.println("PASS-----");
}
}
ExtentReportListener. java
package com.testuatomation.Listeners;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.markuputils.ExtentColor;
import com.aventstack.extentreports.markuputils.MarkupHelper;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;
public class ExtentReportListener {
public static ExtentHtmlReporter report = null;
public static ExtentReports extent = null;
public static ExtentTest test = null;
public static ExtentReports setUp() {
String reportLocation = "./Reports/Extent_Report.html";
report = new ExtentHtmlReporter(reportLocation);
report.config().setDocumentTitle("Automation Test Report");
report.config().setReportName("Automation Test Report");
report.config().setTheme(Theme.STANDARD);
System.out.println("Extent Report location initialized . . .");
report.start();
extent = new ExtentReports();
extent.attachReporter(report);
extent.setSystemInfo("Application", "Youtube");
extent.setSystemInfo("Operating System", System.getProperty("os.name"));
extent.setSystemInfo("User Name", System.getProperty("user.name"));
System.out.println("System Info. set in Extent Report");
return extent;
}
public static void testStepHandle(String teststatus,WebDriver driver,ExtentTest extenttest,Throwable throwable) {
switch (teststatus) {
case "FAIL":
extenttest.fail(MarkupHelper.createLabel("Test Case is Failed : ", ExtentColor.RED));
extenttest.error(throwable.fillInStackTrace());
try {
extenttest.addScreenCaptureFromPath(captureScreenShot(driver));
} catch (IOException e) {
e.printStackTrace();
}
if (driver != null) {
driver.quit();
}
break;
case "PASS":
extenttest.pass(MarkupHelper.createLabel("Test Case is Passed : ", ExtentColor.GREEN));
break;
default:
break;
}
}
public static String captureScreenShot(WebDriver driver) throws IOException {
TakesScreenshot screen = (TakesScreenshot) driver;
File src = screen.getScreenshotAs(OutputType.FILE);
String dest = "C:\\Users\\Prateek.Nehra\\workspace\\SeleniumCucumberBDDFramework\\screenshots\\" + getcurrentdateandtime() + ".png";
File target = new File(dest);
FileUtils.copyFile(src, target);
return dest;
}
private static String getcurrentdateandtime() {
String str = null;
try {
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss:SSS");
Date date = new Date();
str = dateFormat.format(date);
str = str.replace(" ", "").replaceAll("/", "").replaceAll(":", "");
} catch (Exception e) {
}
return str;
}
}
YoutubeChannelValidationsStepDef.java
package com.testautomation.StepDef;
import java.util.Properties;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.GherkinKeyword;
import com.aventstack.extentreports.gherkin.model.Feature;
import com.aventstack.extentreports.gherkin.model.Scenario;
import com.testuatomation.Listeners.ExtentReportListener;
import com.testautomation.PageObjects.YoutubeChannelPage;
import com.testautomation.PageObjects.YoutubeResultPage;
import com.testautomation.PageObjects.YoutubeSearchPage;
import com.testautomation.Utility.BrowserUtility;
import com.testautomation.Utility.PropertiesFileReader;
import cucumber.api.java.After;
import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
public class YoutubeChannelValidationsStepDef extends ExtentReportListener
{
PropertiesFileReader obj= new PropertiesFileReader();
private WebDriver driver;
#Given("^Open Chrome browser with URL$")
public void open_Chrome_browser_with_URL() throws Throwable
{
ExtentTest logInfo=null;
try {
test = extent.createTest(Feature.class, "Youtube channel name validation");
test=test.createNode(Scenario.class, "Youtube channel name validations");
logInfo=test.createNode(new GherkinKeyword("Given"), "open_Chrome_browser_with_URL");
Properties properties=obj.getProperty();
driver=BrowserUtility.OpenBrowser(driver, properties.getProperty("browser.name"), properties.getProperty("browser.baseURL"));
logInfo.pass("Opened chrome browser and entered url");
logInfo.addScreenCaptureFromPath(captureScreenShot(driver));
} catch (AssertionError | Exception e) {
testStepHandle("FAIL",driver,logInfo,e);
}
}
#When("^Search selenium tutorial$")
public void search_selenium_tutorial() throws Throwable
{
ExtentTest logInfo=null;
try {
logInfo=test.createNode(new GherkinKeyword("When"), "search_selenium_tutorial");
new YoutubeSearchPage(driver).NavigateToResultPage("selenium by bakkappa n");
logInfo.pass("Searching selenium tutorial");
logInfo.addScreenCaptureFromPath(captureScreenShot(driver));
} catch (AssertionError | Exception e) {
testStepHandle("FAIL",driver,logInfo,e);
}
}
#When("^Search selenium tutorial \"([^\"]*)\"$")
public void search_selenium_tutorial(String searchString) throws Throwable
{
new YoutubeSearchPage(driver).NavigateToResultPage(searchString);
}
#When("^Click on channel name$")
public void click_on_channel_name() throws Throwable
{
ExtentTest logInfo=null;
try {
logInfo=test.createNode(new GherkinKeyword("When"), "click_on_channel_name");
new YoutubeResultPage(driver).NavigateToChannel();
logInfo.pass("Clicked on the channel name");
logInfo.addScreenCaptureFromPath(captureScreenShot(driver));
} catch (AssertionError | Exception e) {
testStepHandle("FAIL",driver,logInfo,e);
}
}
#Then("^Validate channel name$")
public void validate_channel_name() throws Throwable
{
ExtentTest logInfo=null;
try {
logInfo=test.createNode(new GherkinKeyword("Then"), "validate_channel_name");
String expectedChannelName="1Selenium Java TestNG Tutorials - Bakkappa N - YouTube";
String actualChannelName=new YoutubeChannelPage(driver).getTitle();
Assert.assertEquals(actualChannelName, expectedChannelName,"Channel names are not matching"); //
logInfo.pass("Validated channel title");
logInfo.addScreenCaptureFromPath(captureScreenShot(driver));
System.out.println("closing browser");
driver.quit();
} catch (AssertionError | Exception e) {
testStepHandle("FAIL",driver,logInfo,e);
}
}
}
Your lofInfo is null, should be something like this
#Then("Open Chrome browser with URL")
public void open_Chrome_browser_with_URL() {
try {
logInfo = test.createNode(new GherkinKeyword("Then"), "open_Chrome_browser_with_URL");
//YOUR CODE HERE
logInfo.pass("Chrome opens URL");
}
catch (AssertionError | Exception e) {testStepHandle("FAIL", d, logInfo, e);
}
}
The answer to my question
JaxB reference resolving
led me to further pursue the details of the issue regarding the use of XmlSeeAlso, XmlElementReference and the specification of classes involved in JaxbContext.newInstance.
I started with trying to answer the question:
Is it possible to #XmlSeeAlso on a class without no-args constructor which has #XmlJavaTypeAdapter?
i created the Junit test below. To do so I came accross:
#XmlJavaTypeAdapter w/ Inheritance
#XmlSeeAlso alternative
In this state the code is compilable and runs - it marshals o.k. but does not umarshal as expected.
The marshal result is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message>
<BasicInfo ref="id001"/>
</Message>
Unmarshalling does not create a BasicInfo as would be expected from the code in the Adapter:
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
I find this all very confusing - things seem to be somewhat contradictory and mostly do not work as I'd expect regarding the JaxB settings involved. Most combinations do not work, those that do work do not give the full result yet. I assume that is also depending on the version and implementation being used.
What nees to be done to get this working?
What is a minimal set of XmlElementRef,XmlSeeAlso and JaxbContext newInstance referencing of the necessary classes?
How can I check which JaxB Implementation is being used? I am using EclipseLink 2.3.2? and I'm not sure whether this uses MoxY or not.
Update:
after consideration of:
JAXB xsi:type subclass unmarshalling not working
http://www.java.net/node/654579
http://jaxb.java.net/faq/#jaxb_version
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?
The modified code below is close to what is intended.
Errors that showed up while trying were:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "com.bitplan.storage.jaxb.TestRefId$BasicInfo$BasicInfoRef" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
The code has quite a few comments which can be commented in and out to find out what happens if one tries ...
I still have no clue what the optimal solution would be to avoid superfluous annotations.
modified code:
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
import org.junit.Test;
/**
* Test the Reference/Id handling for Jaxb
*
* #author wf
*
*/
public class TestRefId {
#XmlDiscriminatorNode("#reforid")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapter(RefOrId.Adapter.class)
public static class RefOrId {
#XmlAttribute(name = "reforid")
public String reforid;
public RefOrId() {
}
#XmlAttribute
public String id;
#XmlAttribute
public String ref;
public static class Adapter extends XmlAdapter<RefOrId, RefOrId> {
#Override
public RefOrId marshal(RefOrId idref) throws Exception {
if (idref == null)
return null;
System.out.println("marshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
if (idref instanceof Ref)
return new Id(((Ref) idref).ref);
return idref;
}
#Override
public RefOrId unmarshal(RefOrId idref) throws Exception {
System.out.println("unmarshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
return idref;
}
}
}
#XmlDiscriminatorValue("id")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Id extends RefOrId {
public Id() {
reforid = "id";
}
public Id(String pId) {
this();
this.id = pId;
}
}
#XmlDiscriminatorValue("ref")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Ref extends RefOrId {
public Ref() {
reforid = "ref";
}
public Ref(String pRef) {
this();
this.ref = pRef;
}
}
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a
* -class-without-no-args-constructor-which-has
*/
#XmlRootElement(name = "BasicInfo")
public static class BasicInfo {
public BasicInfo() {
};
public BasicInfo(String pId, String pInfo) {
this.id = new Id(pId);
this.basic = pInfo;
}
// #XmlTransient
public RefOrId id;
public String basic;
}
#XmlRootElement(name = "SpecificInfoA")
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public SpecificInfoA() {
};
public SpecificInfoA(String pId, String pInfo, String pInfoA) {
super(pId, pInfo);
infoA = pInfoA;
}
public String infoA;
}
#XmlRootElement(name = "Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ SpecificInfoA.class, BasicInfo.class })
public static class Message {
public Message() {
};
public Message(BasicInfo pBasicInfo) {
basicInfos.add(pBasicInfo);
}
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
// #XmlElement(name="basicInfo",type=BasicInfoRef.class)
#XmlElementWrapper(name = "infos")
#XmlElement(name = "info")
public List<BasicInfo> basicInfos = new ArrayList<BasicInfo>();
}
/**
* marshal the given message
*
* #param jaxbContext
* #param message
* #return - the xml string
* #throws JAXBException
*/
public String marshalMessage(JAXBContext jaxbContext, Message message)
throws JAXBException {
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml = sw.toString();
return xml;
}
/**
* test marshalling and umarshalling a message
*
* #param message
* #throws JAXBException
*/
public void testMessage(Message message) throws JAXBException {
#SuppressWarnings("rawtypes")
Class[] classes = { Message.class, SpecificInfoA.class, BasicInfo.class }; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
// https://stackoverflow.com/questions/11966714/xmljavatypeadapter-not-being-detected
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
String xml = marshalMessage(jaxbContext, message);
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull(message.basicInfos);
for (BasicInfo binfo : message.basicInfos) {
RefOrId id = binfo.id;
assertNotNull(id);
System.out.println("basicInfo-id " + id.getClass().getSimpleName()
+ " for reforid " + id.reforid);
// assertEquals(message.basicInfo.id, result.basicInfo.id);
}
xml = marshalMessage(jaxbContext, result);
System.out.println(xml);
}
/**
* switch Moxy
*
* #param on
* #throws IOException
*/
public void switchMoxy(boolean on) throws IOException {
File moxySwitch = new File(
"src/test/java/com/bitplan/storage/jaxb/jaxb.properties");
if (on) {
PrintWriter pw = new PrintWriter(new FileWriter(moxySwitch));
pw.println("javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory");
pw.close();
} else {
moxySwitch.delete();
}
}
#Test
public void testStackOverflow8292427() throws JAXBException, IOException {
boolean[] moxyOnList = { false };
for (boolean moxyOn : moxyOnList) {
switchMoxy(moxyOn);
System.out.println("Moxy used: " + moxyOn);
Message message = new Message(new BasicInfo("basicId001",
"basicValue for basic Info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
testMessage(message);
}
}
}
This is the Junit Test as mentioned above (before update):
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.junit.Test;
// Test the Reference/Id handling for Jaxb
public class TestRefId {
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a-class-without-no-args-constructor-which-has
*/
#XmlRootElement(name="BasicInfo")
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class BasicInfo {
#XmlRootElement(name="BasicInfo")
#XmlAccessorType(XmlAccessType.FIELD)
public static class BasicInfoRef {
#XmlAttribute(name = "ref")
public String ref;
}
public static class Adapter extends XmlAdapter<BasicInfoRef,BasicInfo>{
#Override
public BasicInfoRef marshal(BasicInfo info) throws Exception {
BasicInfoRef infoRef = new BasicInfoRef();
infoRef.ref=info.id;
return infoRef;
}
#Override
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
} // Adapter
public String id;
public String basic;
}
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public String infoA;
}
#XmlRootElement(name="Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({SpecificInfoA.class,BasicInfo.class})
public static class Message {
// https://stackoverflow.com/questions/3107548/xmljavatypeadapter-w-inheritance
#XmlElementRefs({
#XmlElementRef(name="basic", type=BasicInfo.class),
// #XmlElementRef(name="infoA", type=SpecificInfoA.class)
})
public BasicInfo basicInfo;
}
#Test
public void testStackOverflow8292427() throws JAXBException {
Message message=new Message();
SpecificInfoA info=new SpecificInfoA();
info.id="id001";
info.basic="basicValue";
info.infoA="infoAValue";
message.basicInfo=info;
#SuppressWarnings("rawtypes")
Class[] classes= {Message.class,SpecificInfoA.class,BasicInfo.class,BasicInfo.BasicInfoRef.class}; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml=sw.toString();
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull("basicInfo should not be null",result.basicInfo);
assertEquals("id001",result.basicInfo.id);
}
}
My JAXB parser suddenly stopped working today. It was working for several weeks. I get the following message. I haven't changed this code for several weeks. Wondering if this set up is good.
EDIT 2: Please could somebody help me! I can't figure this out.
EDIT 1:
My acceptance tests running the same code below are working fine. I believe this is a
classloading issue. I am using the JAXB and StAX in the JDK. However, when I deploy to jboss 5.1, I get the error below. Using 1.6.0_26 (locally) and 1.6.0_30 (dev server). Still puzzling over a solution.
unexpected element (uri:"", local:"lineEquipmentRecord"). Expected
elements are
<{}switchType>,<{}leSwitchId>,<{}nodeAddress>,<{}leId>,<{}telephoneSuffix>,<{}leFormatCode>,<{}groupIdentifier>,<{}telephoneNpa>,<{}telephoneLine>,<{}telephoneNxx>
Here is my unmarshalling class:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
public class PartialUnmarshaller<T> {
XMLStreamReader reader;
Class<T> clazz;
Unmarshaller unmarshaller;
public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException {
this.clazz = clazz;
this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
/* ignore headers */
skipElements(XMLStreamConstants.START_DOCUMENT);
/* ignore root element */
reader.nextTag();
/* if there's no tag, ignore root element's end */
skipElements(XMLStreamConstants.END_ELEMENT);
}
public T next() throws XMLStreamException, JAXBException {
if (!hasNext())
throw new NoSuchElementException();
T value = unmarshaller.unmarshal(reader, clazz).getValue();
skipElements(XMLStreamConstants.CHARACTERS, XMLStreamConstants.END_ELEMENT);
return value;
}
public boolean hasNext() throws XMLStreamException {
return reader.hasNext();
}
public void close() throws XMLStreamException {
reader.close();
}
private void skipElements(Integer... elements) throws XMLStreamException {
int eventType = reader.getEventType();
List<Integer> types = new ArrayList<Integer>(Arrays.asList(elements));
while (types.contains(eventType))
eventType = reader.next();
}
}
This class is used as follows:
List<MyClass> lenList = new ArrayList<MyClass>();
PartialUnmarshaller<MyClass> pu = new PartialUnmarshaller<MyClass>(
is, MyClass.class);
while (pu.hasNext()) {
lenList.add(pu.next());
}
The XML being unmarshalled:
<?xml version="1.0" encoding="UTF-8"?>
<lineEquipment>
<lineEquipmentRecord>
<telephoneNpa>333</telephoneNpa>
<telephoneNxx>333</telephoneNxx>
<telephoneLine>4444</telephoneLine>
<telephoneSuffix>1</telephoneSuffix>
<nodeAddress>xxxx</nodeAddress>
<groupIdentifier>LEN</groupIdentifier>
</lineEquipmentRecord>
<lineEquipmentRecord>
<telephoneNpa>111</telephoneNpa>
<telephoneNxx>111</telephoneNxx>
<telephoneLine>2222</telephoneLine>
<telephoneSuffix>0</telephoneSuffix>
<nodeAddress>xxxx</nodeAddress>
<groupIdentifier>LEN</groupIdentifier>
</lineEquipmentRecord>
</lineEquipment>
Finally, here is MyClass:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* This class is used as an envelope to hold Martens
* line equipment information.
* #author spgezf
*
*/
#XmlRootElement(name="lineEquipmentRecord")
public class MyClass {
private String telephoneNpa;
private String telephoneNxx;
private String telephoneLine;
private String telephoneSuffix;
private String nodeAddress;
private String groupIdentifier;
public MyClass(){
}
// Getters and Setters.
#XmlElement(name="telephoneNpa")
public String getTelephoneNpa() {
return telephoneNpa;
}
public void setTelephoneNpa(String telephoneNpa) {
this.telephoneNpa = telephoneNpa;
}
#XmlElement(name="telephoneNxx")
public String getTelephoneNxx() {
return telephoneNxx;
}
public void setTelephoneNxx(String telephoneNxx) {
this.telephoneNxx = telephoneNxx;
}
#XmlElement(name="telephoneLine")
public String getTelephoneLine() {
return telephoneLine;
}
public void setTelephoneLine(String telephoneLine) {
this.telephoneLine = telephoneLine;
}
#XmlElement(name="telephoneSuffix")
public String getTelephoneSuffix() {
return telephoneSuffix;
}
public void setTelephoneSuffix(String telephoneSuffix) {
this.telephoneSuffix = telephoneSuffix;
}
#XmlElement(name="nodeAddress")
public String getNodeAddress() {
return nodeAddress;
}
public void setNodeAddress(String nodeAddress) {
this.nodeAddress = nodeAddress;
}
#XmlElement(name="groupIdentifier")
public String getGroupIdentifier() {
return groupIdentifier;
}
public void setGroupIdentifier(String groupIdentifier) {
this.groupIdentifier = groupIdentifier;
}
}
Thanks, this is classloading issue I couldn't overcome so I abandoned JAXB.
Your PartialUnmarshaller code worked for me. Below is an alternate version that changes the skipElements method that may work better.
import java.io.InputStream;
import java.util.NoSuchElementException;
import javax.xml.bind.*;
import javax.xml.stream.*;
public class PartialUnmarshaller<T> {
XMLStreamReader reader;
Class<T> clazz;
Unmarshaller unmarshaller;
public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException {
this.clazz = clazz;
this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
/* ignore headers */
skipElements();
/* ignore root element */
reader.nextTag();
/* if there's no tag, ignore root element's end */
skipElements();
}
public T next() throws XMLStreamException, JAXBException {
if (!hasNext())
throw new NoSuchElementException();
T value = unmarshaller.unmarshal(reader, clazz).getValue();
skipElements();
return value;
}
public boolean hasNext() throws XMLStreamException {
return reader.hasNext();
}
public void close() throws XMLStreamException {
reader.close();
}
private void skipElements() throws XMLStreamException {
while(reader.hasNext() && !reader.isStartElement()) {
reader.next();
}
}
}