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).
Related
I am writting test for a try catch block, but I am quite confused about how to test the catch block...especially it uses slf4j to log the error.
addText here is the another method from the same class.
public class TextQueue {
public void addTextToQueue(final Text text) {
try {
if (text != null) {
addText(text);
}
} catch (final JsonProcessingException e) {
LOGGER.error("Error adding text to the queue : {}", e);
}
}
}
here is my test case
#RunWith(MockitoJUnitRunner.class)
public class TextQueueTest {
private org.slf4j.Logger LOGGER = LoggerFactory.getLogger(TextQueueTest.class);
private static final String MY_TEXT = "src/text.json";
private Text text;
private final ObjectMapper mapper = new JacksonConfig().dateAsStringObjectMapper();
#Mock
private TextQueue textQueue;
#Before
public void setUp() throws IOException {
text = mapper.readValue(new File(TextQueueTest.MY_TEXT), Text.class);
}
#Test
public void addTextToQueue() {
try{
textQueue = spy(textQueue);
textQueue.addTextToQueue(text);
}catch(final Exception e){
LOOGER.error("add text to queue threw an error" + e);
}
}
can anyone help me solve this problem?
First of all, you should really read a good tutorial about Mockito, like the one from vogella. You see, you are simply throwing together a lot of things that are non-sensical.
Like:
#Mock
private TextQueue textQueue;
to then have
textQueue = spy(textQueue);
within your test case. You should be really clear about this. A spy is build on a real instance of your class under test. Creating a spy that spies on a mock, as said: that makes no sense.
Then:
}catch(final Exception e){
Logger.error("add text to queue threw an error" + e);
Again, non-sensical. The whole idea of your unit tests is that they fail when something is wrong. When your production code throws unexpected exceptions, you don't log them, you just let them fail your test case in the end.
To answer the actual question: it looks like your production code is using a specific "constant" logger instance. Given that design, the only way to check your production code would be to:
make that LOGGER a mocked object
somehow inject it into an instance underTest of your production code class
trigger that method to test on underTest (and somehow force the method to throw an exception)
verify that the mocked LOGGER saw the expected call to error()
We can't give better advise, because your code input isn't sufficient, we don't really know what your production class is doing (for example: we don't know what LOGGER is, and where it is coming from. if it happens to be a static variable, then most likely, you can't get "control" over it with Mockito).
In any case, you probably actually need the spy concept. In order to test addTextToQueue() you need a way to invoke the "real" addTextToQueue() implementation, but the call to addTser() within needs to go to a mock (so that you can control what that call does).
But as said: start by really researching how Mockito works, instead of throwing together things that make no sense in some "trial and error" approach. Correct unit testing with mocking is complicated, you can't learn that by "trial and error".
I just want to test if an exception with a given message is being thrown using google-truth.
Is quite easy to do that using junit using #Test(expected=, but I'm unable to figure out how to do that with truth. There are no samples around ThrowableSubject.
Should I stick with plain JUnit for these kind of tests?
[updated]
The Truth authors recommend using JUnit 4.13/5's assertThrows() mechanism, since this doesn't really need support in Truth. This would look more like:
SpecificException e =
assertThrows(SpecificException.class, () -> doSomethingThatThrows());
assertThat(e).hasMessageThat().contains("blah blah blah");
assertThat(e).hasCauseThat().isInstanceOf(IllegalStateException.class);
assertThat(e).hasCauseThat().hasMessageThat().contains("blah");
This is recommended over try/fail/catch as it is terser, avoids the "missing fail" problem, and returns an object that can be asserted-on using the ThrowableSubject in Truth.
If you do not have assertThrows(), then please use the try/fail/catch pattern, as this is clear and explicit.
try {
doSomethingThatThrows();
fail("method should throw");
} catch (SpecificException e) {
// ensure that e was thrown from the right code-path
// especially important if it's something as frequent
// as an IllegalArgumentException, etc.
assertThat(e).hasMessage("blah blah blah");
}
While #Rule ExpectedException and #Test(exception=...) exist in JUnit, these aren't recommended by the Truth team, insofar as they have some subtle (and less subtle) ways you can write tests that pass but which should fail.
While this is also true of try/fail/catch, internally Google mitigates this with the use of error-prone, which provides a static compile-time check to ensure that this pattern doesn't omit the fail(), etc. It is highly recommended that you use error-prone or another static analysis check to catch these. Sadly, the rule-based and annotation-based methods aren't as easily amenable to static analysis as this try/catch block.
As an update here, we've moved away from the pattern Christian described, and Issue #219 has been closed in favor of JUnit's expectThrows() (coming in 4.13, similar methods already exists in TestNG's Assert).
In tandem with expectThrows() you can use Truth to make assertions about the thrown exception. So Christian's example would now be:
SpecificException expected = expectThrows(
SpecificException.class, () -> doSomethingThatThrows());
assertThat(expected).hasMessageThat().contains("blah blah blah");
There is currently no built-in way to verify an expected Exception with google-truth. You can do one of the following:
Use one of the JUnit approaches, such as expected=, as you mentioned
Write tests the "ugly" way by surrounding the exercise/act/when portion of your test with a try...catch, as #c0der mentioned, which is what the unit tests for guava do
Make your own fluent assertions, similar to what AssertJ already has, like the following example
I believe google-truth does not have any similar functionality because it supports Java 1.6.
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
import com.google.common.truth.SubjectFactory;
import org.junit.Test;
import java.util.concurrent.Callable;
import static com.google.common.truth.Truth.assertAbout;
public class MathTest {
#Test
public void addExact_throws_ArithmeticException_upon_overflow() {
assertAbout(callable("addExact"))
.that(() -> Math.addExact(Integer.MAX_VALUE, 1))
.willThrow(ArithmeticException.class);
}
static <T> SubjectFactory<CallableSubject<T>, Callable<T>> callable(String displaySubject) {
return new SubjectFactory<CallableSubject<T>, Callable<T>>() {
#Override public CallableSubject<T> getSubject(FailureStrategy fs, Callable<T> that) {
return new CallableSubject<>(fs, that, displaySubject);
}
};
}
static class CallableSubject<T> extends Subject<CallableSubject<T>, Callable<T>> {
private final String displaySubject;
CallableSubject(FailureStrategy failureStrategy, Callable<T> callable, String displaySubject) {
super(failureStrategy, callable);
this.displaySubject = displaySubject;
}
#Override protected String getDisplaySubject() {
return displaySubject;
}
void willThrow(Class<?> clazz) {
try {
getSubject().call();
fail("throws a", clazz.getName());
} catch (Exception e) {
if (!clazz.isInstance(e)) {
failWithBadResults("throws a", clazz.getName(), "throws a", e.getClass().getName());
}
}
}
}
}
I am trying to execute a data driven test using TestNG (& of course the dataprovider annotation).
My scenario is something like this ...
Use dataProvider to have a 2 dim array. (I am using this to read from Excel, but avoided it for brevity of the question).
#DataProvider(name = "featureTest")
public Object[][] dataSets() throws Exception {
return new Object[][] { {"TC_01", "testuser_1", "Test#123", "ABC Street", "123-456-7899" },
{ "TC_02", "testuser_1", "Test#123", "PQR Street", "222-456-7899" }
};
}
In the #Test method, there are several methods as per the functional flow -
#Test(dataProvider = "featureTest")
public void executeTest(String... data) throws Exception {
try{
feature_1.execute(data);
feature_2.execute(data);
feature_3.execute(data);
feature_4.execute(data);
}
catch(Exception e){
log.error("Error has occured");
}
}
Now my main problem is that the functional error can occur anywhere out of these 4 (n) methods that I specify in my #Test.
In case of an exception in any of the methods, I need to "Skip" the particular dataset and proceed to the next one.
For eg: In during execution of TC_01, an exception occured in feature_2.execute(), it should not execute the feature_3 and feature_4 methods.
Note:
I tried handling it using #BeforeMethod, #AfterMethod but still it goes through the unwanted methods that I want to avoid.
Thanks in advance for your help/inputs && apologies for the long question although a relatively simple concept to explain !!!
Its very simple. Use return!
Use a condition or try catch to evaluate the failure and then use a return statement;
One approach I can think of is the factory approach,
Your testclass
class Test{
Data data;
Test(Data){
this.data=data;
}
#Test
test1(){
feature_1.execute(data);
}
#Test
test2(dependsOnMethods ="test1"){
feature_2.execute(data);
}
#Test(dependsOnMethods ="test2")
test3(){
feature_3.execute(data);
}
#Test(dependsOnMethods ="test3")
test4(){
feature_4.execute(data);
}
}
And in your factory class
class Factory{
#Factory(DataProvider = "myDP")
public Object[] factoryTest(Data data){
new Test(data);
}
#DataProvider
public Object [][] myDP(){
enter code here
}
}
Does Webdriver 2.28 automatically take a screenshot on exception/fail/error?
If it does, where can I find the screenshot? Which directory is the default?
No, it doesn't do it automatically. Two options you can try:
Use WebDriverEventListener that is attachable to a EventFiringWebDriver which you can simply wrap around your usual driver. This will take a screenshot for every Exception thrown by the underlying WebDriver, but not if you fail an assertTrue() check.
EventFiringWebDriver driver = new EventFiringWebDriver(new InternetExplorerDriver());
WebDriverEventListener errorListener = new AbstractWebDriverEventListener() {
#Override
public void onException(Throwable throwable, WebDriver driver) {
takeScreenshot("some name");
}
};
driver.register(errorListener);
If you're using JUnit, use the #Rule and TestRule. This will take a screenshot if the test fails for whatever reason.
#Rule
public TestRule testWatcher = new TestWatcher() {
#Override
public void failed(Throwable t, Description test) {
takeScreenshot("some name");
}
};
The takeScreenshot() method being this in both cases:
public void takeScreenshot(String screenshotName) {
if (driver instanceof TakesScreenshot) {
File tempFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(tempFile, new File("screenshots/" + screenshotName + ".png"));
} catch (IOException e) {
// TODO handle exception
}
}
}
...where the FileUtils.copyFile() method being the one in Apache Commons IO (which is also shipped with Selenium).
WebDriver does not take screenshot itself. But you cat take by this way : ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);
Then save the screenshot anywhere you want.
Also you cat use something like Thucydides, wich may take screenshot on each action or on error and put it in a pretty report.
The short answer is No. WebDriver is an API to interact with the browser. You can make screenshots with it but you should know when to do it. So it's not done automatically as WebDriver doesn't know anything about testing.
If you are using TestNG as testing library you can implement Listener whose methods will be executed on different events (failure, success or other). In these methods you can implement the required logic (e.g. making screenshots).
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".