I am testing with the wonderful TestNG-Framework. My question is if it is possible to set the annotations for #Test-annotation in the testng.xml-configuration file?
I don't want to hard-code the #Test-annotation like
#Test(dataProvider = "dataFileProvider", dataProviderClass = TestDataProvider.class)
I want to configure it in the testng.xml
I have got two ideas on this case:
Workaraound 1: StaticProvider
You can easily change the Static Provider if needed
Workaraound 2: Annotation Transformer
Never tried that but should work even if have to grab the XML- data manually
Looking forward to Mr. Beust's answer... ;)
The short answer is: no, you can't add annotations to your code from testng.xml.
You can modify existing annotations with an Annotation Transformer, as explained by Frank.
Sometimes, you just really want to do something and you can't, like accessing private variables to fix memory leaks. Figuring out how to do things like this, despite the fact that you can't are fun. In case, you really want to, I might suggest trying to run your suite using the TestNG object and before running loading the testng.xml file.
Personally, I like using 'mvn test' and unfortunately, adding the pom.xml code to run from a testng xml file will require that you supply a testng.xml file, so 'mvn test' won't work. Always make sure what 95% of programmers use works, then allow overridding.
So, I might suggest extending the testng.xml file yourself and writing some code to read the testng.xml file and configure annotations using the annotation transformer class.
Here is some code to get you started:
public class TestNGSuite {
public static void main(String[] args) {
System.out.println("main start");
try {
new TestNGSuite(new Class[]{ Demo.class });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("main finish");
}
public TestNGSuite(Class[] classes) throws Exception {
// Create Suite List
List<XmlSuite> suites = new ArrayList<XmlSuite>();
// Add Suite to Suite List
XmlSuite suite = new XmlSuite();
suites.add(suite);
suite.setName("MyTestSuite");
// Add Test to Suite
XmlTest test = new XmlTest(suite);
test.setName("MyTest");
// Add Class List to Test
List<XmlClass> xmlClasses = new ArrayList<XmlClass>();
test.setXmlClasses(xmlClasses);
// Add Class to Class List
for(Class clazz: classes) {
XmlClass xmlClass = new XmlClass(clazz);
xmlClasses.add(xmlClass);
}
// Run TestNG
TestNG testNG = new TestNG();
testNG.setXmlSuites(suites);
testNG.addListener(new TestNGAnnotationTransformer(methodName));
testNG.addListener(new TestNGSuiteConsoleLogger());
testNG.run();
if(testNG.hasFailure()) { // Throw an exception to make mvn goal fail
throw new Exception("Failed Tests");
}
}
public static class TestNGSuiteConsoleLogger extends TestListenerAdapter{
#Override
public void onTestFailure(ITestResult tr) {
Console.log(TestNGSuiteConsoleLogger.class, "FAILURE:"+tr.getMethod());
tr.getThrowable().printStackTrace();
}
}
public static class TestNGAnnotationTransformer implements IAnnotationTransformer{
String methodToRun;
public TestNGAnnotationTransformer(String methodName) {
methodToRun = methodName;
}
public void transform(ITestAnnotation annotation, Class arg1,
Constructor arg2, Method testMethod) {
if (methodToRun.equals(testMethod.getName())) {
annotation.setEnabled(true);
}
}
}
}
If you want to run Demo.class, make sure there is a method there with the TestNG annotation "#Test".
Related
How do you mock file reading/writing via JUnit?
Here is my scenario
MyHandler.java
public abstract class MyHandler {
private String path = //..path/to/file/here
public synchronized void writeToFile(String infoText) {
// Some processing
// Writing to File Here
File file = FileUtils.getFile(filepath);
file.createNewFile();
// file can't be written, throw FileWriteException
if (file.canWrite()) {
FileUtils.writeByteArrayToFile(file, infoText.getBytes(Charsets.UTF_8));
} else {
throw new FileWriteException();
}
}
public String readFromFile() {
// Reading from File here
String infoText = "";
File file = new File(path);
// file can't be read, throw FileReadException
if (file.canRead()) {
infoText = FileUtils.readFileToString(file, Charsets.UTF_8);
} else {
throw FileReadException();
}
return infoText
}
}
MyHandlerTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest({
MyHandler.class
})
public class MyHandlerTest {
private static MyHandler handler = null;
// Some Initialization for JUnit (i.e #Before, #BeforeClass, #After, etc)
#Test(expected = FileWriteException.class)
public void writeFileTest() throws Exception {
handler.writeToFile("Test Write!");
}
#Test(expected = FileReadException.class)
public void readFileTest() throws Exception {
handler.readFromFile();
}
}
Given above source, Scenario when file is not writable (write permission not allowed) is OK, However, when i try to do scenario wherein file is not readable (read permission not allowed). It always read the file, i have already tried to modify the file permission on the test code via below
File f = new File("..path/to/file/here");
f.setReadable(false);
However, I did some reading, setReadable() always returns false (failed) when run on Windows machine.
Is there a way to modify the file permission of the target file programmatically in relation to JUnit?
Note
Target source code to test cannot be modified, meaning
Myhandler.class is a legacy code which is not to be modified.
Instead of relying on the operating system file permissions, use PowerMock to mock FileUtils.getFile(...) and make it return an instance of File (e.g. anonymous sub class) that returns a specific value for canWrite()/canRead().
Mocking static methods with Mockito
Since Mockito cannot mock static methods, use a File factory instead (or refactor your FileUtils to be a factory), then you can mock it and return a mocked File instance as well, where you can also mock any File methods you want.
So instead of FileUtils.getFile(filepath) you will now have something like FileFactory.getInstance().getFile(filepath) for example, where you can mock getFile(String) method easily.
In jUnit there's a handy rule for scenarios like yours.
public class MyHandlerTest {
#Rule
// creates a temp folder that will be removed after each test
public org.junit.rules.TemporaryFolder folder = new org.junit.rules.TemporaryFolder();
private MyHandler handler;
#Before
public void setUp() throws Exception {
File file = folder.newFile("myFile.txt");
// do whatever you need with it - fill with test content and so on.
handler = new MyHandler(file.getAbsolutePath()); // use the real thing
}
// Test whatever behaviour you need with a real file and predefined dataset.
}
I'm searching around to see if what I want to accomplish is possible with annotations.
Basically, we have a bunch of TestNG test cases which we're micro managing
Example:
#Test
public void reportingTest(){
Assert.true(false);
}
The above would simply fail, but we wrap everything in an assertion try catch.
#Test
public void reportingTest(){
try {
Assert.true(false);
Report.batch(Enum.Pass, 106);
} catch (Throwable t) {
Report.batch(Enum.Fail, 106, "Test case has failed");
}
}
However, after hundreds of test-cases... having that try catch is super cumbersome.
I am trying to accomplish something like this
#Reporting(id=106)
#Test
public void reportingTest(){
Assert.true(false);
}
Inside of the annotation I would have the ability to capture the failed assertion and send a log off based on my id.
Thanks!
TestNG provides listeners and the one you are looking for may be the TestListener.
Your annotation will be available from there: tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Reporting.class).
I figured out how to use the Ant API to run a JUnit Test and create an XML of the result.
String pathToReports = "/tmp/junitreports";
Project project = new Project();
JUnitTest test = null;
try
{
new File(pathToReports).mkdir();
JUnitTask task = new JUnitTask();
project.setProperty("java.io.tmpdir",pathToReports);
task.setProject(project);
FormatterElement.TypeAttribute type = new FormatterElement.TypeAttribute();
type.setValue("xml");
FormatterElement formater = new FormatterElement();
formater.setType(type);
task.addFormatter(formater);
test = new JUnitTest(TestClass.class.getName());
test.setTodir(new File(pathToReports));
task.addTest(test);
task.execute();
}
...
TestClass:
public class TestClass
{
#Test
public void test()
{
fail("failed");
}
}
The Code works just fine. The XML is created and I can see that the test "failed".
Now my question: is there any way to also get the test results programatically? I expected to get an updated version of the JUnitTest object somehow where I can call the method "failureCount()".
test.failurecount() after execution of the task returns 0 of course. Parsing the XML seems odd to me as the number of failures should already be stored somewhere.
You could have a variable (int failedTests) increment each time a test fails. You could do this by using a TestWatcher rule.
After all the tests have run you could print it out (or do whatever you wanna do with it...) with:
#AfterClass
public static void printFailedTestsCount() {
System.out.println(failedTests + " tests failed.");
}
I'm Running a method annotated with #Test and I want get a reference to the object JunitCore, this object invokes the method by reflection.
How can I get a reference to the that object, If It's possible?(maybe a security issue)
I tried reflection and classLoader but I couldn't make it work.
Thanks
The JUnitCore is a basic entry point for Junit tests. The way it works is it finds a List of classes provided as java command arguments and uses them to create a Runner with which it runs the test cases.
At no point during processing does the main method in JUnitCore ever pass a reference of the JUnitCore instance it creates to any other object. As such, it is not retrievable either directly or with reflection.
JUnitCore is as follows
public static void main(String... args) {
runMainAndExit(new RealSystem(), args);
}
public static void runMainAndExit(JUnitSystem system, String... args) {
Result result= new JUnitCore().runMain(system, args);
system.exit(result.wasSuccessful() ? 0 : 1);
}
public Result runMain(JUnitSystem system, String... args) {
system.out().println("JUnit version " + Version.id());
List<Class<?>> classes= new ArrayList<Class<?>>();
List<Failure> missingClasses= new ArrayList<Failure>();
for (String each : args)
try {
classes.add(Class.forName(each));
} catch (ClassNotFoundException e) {
system.out().println("Could not find class: " + each);
Description description= Description.createSuiteDescription(each);
Failure failure= new Failure(description, e);
missingClasses.add(failure);
}
RunListener listener= new TextListener(system);
addListener(listener);
Result result= run(classes.toArray(new Class[0]));
for (Failure each : missingClasses)
result.getFailures().add(each);
return result;
}
... // and more
No where in this implementation is a reference to this passed as an argument. As such, you cannot get a reference to it.
The only way is to create a JunitCore instance and run the tests yourself:
JUnitCore junit = new JUnitCore();
//we can add a listener to listen for events as we run the tests
junit.addListener(new RunListener(){
#Override
public void testFailure(Failure failure) throws Exception {
System.out.println("failed " + failure);
}
});
Result result = junit.run(Class.forName(nameOfTestSuite));
Is it possible to create a report from JUnit without Ant or Maven? Because I call the tests with velocitycode, and the velocitycodes calls a method. And that method calls all the tests. So I can get a response from it, the failures/errors/runs etc. But I want to create a report with it.. Or do I need to create html stuff by myself?
I created the methods and testmethods in Java, so I will do everything in Java, except the call, thats in Velocity code.
Velocitycode:
${custom.test}
Java code:
public void getTest(){
junit.textui.TestRunner runner = new junit.textui.TestRunner();
TestResult testresult = Junit.textui.TestRunner.run(runner.getTest(MyTestClass.class.getName()));
}
You will need the ant library. But with this code you can create an XML report and use it in other pograms. Such as Jenkins.
public static void getTest(){
String pathToReports = "C:\\path\\to\\the\\Reports";
Project project = new Project();
try {
new File(pathToReports).mkdir();
JUnitTask task = new JUnitTask();
project.setProperty("java.io.tmpdir",pathToReports);
task.setProject(project);
FormatterElement.TypeAttribute type = new FormatterElement.TypeAttribute();
type.setValue("xml");
FormatterElement formater = new FormatterElement();
formater.setType(type);
task.addFormatter(formater);
JUnitTest test = new JUnitTest(YOURTEST.class.getName());
test.setTodir(new File(pathToReports));
task.addTest(test);
task.execute();
} catch (Exception e) {
}
}
Don't think so. But you might be able to use ant as a library instead of a tool, and use the same code that the tool uses to generate these reports.