I am trying to get some context of the result of the test run in the #AfterTest. I would like to have, at bare minimum, knowledge if it passed or not and ideally also the thrown exception if there is one.
However, every parameter I try seems to not be resolvable and I can't find any documentation on what should be available.
Code:
public class TestClass {
#AfterEach
public void afterEach(
TestInfo testInfo, //works, but no report on state of test
// none of these work
TestExecutionSummary summary,
TestExecutionResult result,
TestFailure fail,
Optional<Throwable> execOp,
Throwable exception
) {
// ...
}
}
What can I do to get this context?
Not sure if this is what you want, but you can use either a TestExecutionListener or a TestWatcher (there are also other ways that you can check in documentation).
An example of TestWatcher can be found here: TestWatcher in junit5 and a more detailed explanation here: https://www.baeldung.com/junit-testwatcher.
The following code example was partially taken from here.
public class TestResultLoggerExtension implements TestWatcher, AfterAllCallback {
...
#Override
public void testSuccessful(ExtensionContext context) {
System.out.println("Test Successful for test {}: ", context.getDisplayName();
}
#Override
public void testFailed(ExtensionContext context, Throwable cause) {
System.out.println("Test Failed for test {}, with cause {}", context.getDisplayName(), cause.getMessage());
}
}
You test class would be something like this:
#ExtendWith(TestResultLoggerExtension.class)
public class TestClass {
You can adapt the logic to your needs.
More References:
https://junit.org/junit5/docs/current/user-guide/#extensions-test-result-processing
https://junit.org/junit5/docs/current/user-guide/#launcher-api-listeners-custom
Related
in spring test, I know I can mock static method(usually static util methods: generate id, get value from Redis) using Mockito like:
try (MockedStatic) {
}
but having to do this in every test method is ugly and cumbersome, is there any way to do it all(i am ok to have a single mocked behavior)
I am thinking maybe a junit5 extension, or Mockito extension, this seems like a common problem, I wonder if anyone tries something with any success.
try this
public class StaticClassTest {
MockedStatic<YourStatic> mockedStatic;
#Before
public void setup() {
mockedStatic = Mockito.mockStatic(YourStatic.class);
// if you want the same behavior all along.
mockedStatic.when(() -> YourStatic.doSomething(anyString())).thenReturn("TEST");
}
#Test
public void test_static() {
// write your test here
}
#After
public void teardown() {
mockedStatic.close();
}
}
We defined one testng result listener which help us to send the testing result for each test case defined in testng.xml to one internal tool such like below:
public class TestResultsListener implements ITestListener, ISuiteListener {
#Override
public void onFinish(ISuite suite){
// some code to send the final suite result to internal tools
}
#Override
public void onTestSuccess(ITestResult iTestResult) {
this.sendResult(iTestResult,"PASS");Result
}
private void sendStatus(ITestResult iTestResult, String status){
// Set test case information
......
jsonArr.add(testResult);
}
}
And then we integrated this listener to other project's testng xml file such like:
<listeners>
<listener class-name="com.qa.test.listener.TestesultsListener" />
</listeners>
It worked as designed: once the test suite finishes, the test result will be uploaded to internal tools.
Now we have one requirement that in one project, one test case in testng.xml is related to 3 test cases in internal tool which means that for one test case in testng.xml we need to update 3 test cases in internal tools. How can we update our current testng listener to fulfill this?
Thanks a lot.
You can annotate each of your tests with the list of corresponding internal test tool ids:
Here I suppose that you have 2 testng tests: one is related to internal test IT-1, and the other one to internal tests IT-2, IT-3 and IT-4:
#Listeners(MyTestListener.class)
public class TestA {
#Test
#InternalTool(ids = "IT-1")
public void test1() {
System.out.println("test1");
fail();
}
#Test
#InternalTool(ids = {"IT-2", "IT-3", "IT-4"})
public void test2() {
System.out.println("test2");
}
}
The annotation is simply defined like this:
#Retention(RetentionPolicy.RUNTIME)
public #interface InternalTool {
String[] ids();
}
The your listener has just to figure out which annotation are present on successful/failed tests:
public class MyTestListener extends TestListenerAdapter implements ITestListener {
#Override
public void onTestSuccess(ITestResult tr) {
super.onTestSuccess(tr);
updateInternalTool(tr, true);
}
#Override
public void onTestFailure(ITestResult tr) {
super.onTestFailure(tr);
updateInternalTool(tr, false);
}
private void updateInternalTool(ITestResult tr, boolean success) {
InternalTool annotation = tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(InternalTool.class);
for (String id : annotation.ids()) {
System.out.println(String.format("Test with id [%s] is [%s]", id, success ? "successful" : "failed"));
}
}
}
The following output is produced:
test1
Test with id [IT-1] is [failed]
test2
Test with id [IT-2] is [successful]
Test with id [IT-3] is [successful]
Test with id [IT-4] is [successful]
You can also extend this mechanism to Suite listeners as well.
Disclaimer: The line
InternalTool annotation = tr.getMethod().getConstructorOrMethod().getMethod().getAnnotation(InternalTool.class); is not bullet-proof (high risk of null pointer exception). Should be more robust.
I am currently writing a test in TestNG. Currently I have the following method:
#Parameters({"clickRememberMe"})
#Test
public void runTest(String clickRememberMe) {
}
Now I have found that a TestNG class called org.testng.ITestContext can be used to share data between tests, which I want to use. The tutorial showing it told me to do this:
#Test
public void runTest(ITestContext itc){
}
And this also works. However, now I want to use parameters together with the ITestContext like follows:
#Parameters({"clickRememberMe"})
#Test
public void runTest(ITestContext itc, String clickRememberMe) {
}
Sadly, this throws the following exception:
java.lang.IllegalArgumentException: argument type mismatch
Is there any way to use #Parameters together with ITestContext in TestNG?
I already found the solution. Instead of:
#Parameters({"clickRememberMe"})
#Test
public void runTest(ITestContext itc, String clickRememberMe) {
}
I had to put the ITestContext after the parameters, like this:
#Parameters({"clickRememberMe"})
#Test
public void runTest(String clickRememberMe, ITestContext itc) {
}
abort()-Method:
public void abort() {
LOG.info("some-text");
warning("some-text");
}
warning()-Method:
public void warning(String message) {
FacesContext.getCurrentInstance()
.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "INFO:", message));
}
I want to write a Test-Case for abort wich is just verifying that nothing has changed and a second Test-Case which is verifying that warning() is working.
I´m knowing this little two methods doesn´t need a Unit-Test but I want to know if it´s possible. UI-Test for showing the p:message is working well but I want to check the Caption, Typ and Message by Unittest before because it´s running much faster.
working solution with just using JUnit 4.11
Separate the content from warning() in a own class like this:
#Named
#RequestScoped
public class Resources {
#Produces
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
}
Next you need to define an ArgumentCaptor which can catch the FacesMessage for your JUnit-Test. I´ve created it as a clss member which will be initialized in the #before section and get the null value in #teardown.
private ArgumentCaptor<FacesMessage> facesMessageCaptor;
#Before
public void setUp() {facesMessageCaptor = ArgumentCaptor
.forClass(FacesMessage.class);
}
#After
public void tearDown() { facesMessageCaptor = null; }
Now you just need to add two #Mocks to your test-class
#Mock
Resources resourcesMock;
#Mock
FacesContext facesContextMock;
So you did it! Write the test like this:
Mockito.doReturn(facesContextMock).when(resourcesMock).produceFacesContext();
// Execute Method
cut.warning("SOME_DETAIL_TEXT");
// Verify interactions with the Resources and Faces and maybe others...
verify(resourcesMock).produceFacesContext();
verify(facesContextMock).addMessage(Mockito.anyString() ,
facesMessageCaptor.capture());
verifyNoMoreInteractions(...., resourcesMock, facesContextMock);
// write assert (i´ve used hamcrast-corematchers - more readable)
actualMessage = (FacesMessage) facesMessageCaptor.getValue();
assertThat(FacesMessage.SEVERITY_WARN, is(equalTo(actualMessage.getSeverity())));
assertThat(MY_TITLE, is(equalTo(actualMessage.getSummary())));
assertThat("DETAIL_TEXT", is(equalTo(actualMessage.getDetail())));
Suppose that I have a class like;
public class FooBar {
public int getMethod(List<String> code){
if(code.size() > 100)
throw new Exception;
return 0;
}
}
and I have a test class like this;
#RunWith(PowerMockRunner.class)
#PrepareForTest(FooBar.class)
public class FooBarTest{
FooBar fooBarInstance;
#Before
public void setUp() {
//MockitoAnnotations.initMocks(this);
fooBarInstance = new FooBar();
}
#Test(expected = Exception.class)
public void testGetCorrelationListCodesParameter() {
List<String> codes = Mockito.spy(new ArrayList<String>());
Mockito.doReturn(150).when(codes).size();
fooBarInstance.getMethod(codes);
}
}
How can I make this test method to throw an exception ? I've dealing for hours to do this. Well thanks anyway.
Spying is not needed, mocking is enough. As #David said, also mocking is not needed and not recommended for value object.
Using #Test(expected = Exception.class) has many drawbacks, test can pass when exception is thrown from not expected places. Test is not working but is visible as green.
I prefer BDD style testing with catch-exception.
Reasons for using catch-exceptions
(...) in comparison to the use of try/catch blocks.
The test is more concise and easier to read.
The test cannot be corrupted by a missing assertion. Assume you forgot to type fail() behind the method call that is expected to throw an exception.
(...) in comparison to test runner-specific mechanisms that catch and verify exceptions.
A single test can verify more than one thrown exception.
The test can verify the properties of the thrown exception after the exception is caught.
The test can specify by which method call the exception must be thrown.
The test does not depend on a specific test runner (JUnit4, TestNG).
import static com.googlecode.catchexception.CatchException.caughtException;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;
public class FooBarTest {
FooBar sut = new FooBar(); // System Under Test
#Test
public void shouldThrowExceptionWhenListHasTooManyElements() {
when(sut).getMethod(listWithSize(150));
then(caughtException()).isInstanceOf(Exception.class);
}
private List<String> listWithSize(int size) {
return new ArrayList<String>(Arrays.asList(new String[size]));
}
}
Full working code for this test: https://gist.github.com/mariuszs/8543918
Not recommended solution with expected and mocking.
#RunWith(MockitoJUnitRunner.class)
public class FooBarTest {
#Mock
List<String> codes;
FooBar fooBarInstance = new FooBar();
#Test(expected = Exception.class)
public void shouldThrowExceptionWhenListHasTooManyElements() throws Exception {
when(codes.size()).thenReturn(150);
fooBarInstance.getMethod(codes);
}
}
A list is a value object. It's not something we should mock. You can write this whole test without mocking anything, if you're prepared to build a list that has a size in excess of 100.
Also, I prefer to use JUnit's ExpectedException mechanism, because it lets you check which line of the test method threw the exception. This is better than passing an argument to the #Test annotation, which only lets you check that the exception was thrown somewhere within the method.
public class FooBarTest {
#Rule
public ExpectedException exceptionRule = ExpectedException.none();
private FooBar toTest = new FooBar();
#Test
public void getMethodThrowsException_whenListHasTooManyElements() {
List<String> listWith101Elements =
new ArrayList<String>(Arrays.asList(new String[101]));
exceptionRule.expect(Exception.class);
toTest.getMethod(listWith101Elements);
}
}