Geb Reporter / Extension to retrieve Test Status - groovy

I am attempting to replace some custom java selenium extensions by utilizing geb. I have hit a bit of a brick wall when I attempt to utilize a grid in the cloud (i.e. SauceLabs). When my tests complete, it'd be nice to send an update back to indicate whether or not the test has failed or succeeded. To utilize this, I need the sessionId from the RemoteWebDriver instance. This can be obtained in a custom Reporter, however I can't determine the success with this interface. Since I am extending the GebReportingSpec, I attempted to create my own custom version, which had a custom Junit rule to track success or failure:
public class TestSuccess extends TestWatcher {
boolean success;
String message;
#Override
protected void starting(Description d) {
message = d.getMethodName();
}
#Override
protected void succeeded(final Description description) {
System.out.println("Test Success [succeeded] " + description);
this.success = true;
}
#Override
protected void failed(final Throwable e, final Description description) {
System.out.println("Test Success [failed] " + description);
this.success = false;
}
public boolean isSuccess() {
return success;
}
#Override
public String toString() {
return message + " success: <" + success + ">.";
}
}
I then added that to my CustomReportingSpec:
class CustomReportingSpec extends GebReportingSpec {
/* I also tried creating this as a RuleChain with:
* #Rule TestRule chain = RuleChain.outerRule(
super._gebReportingSpecTestName).around(new TestSuccess());
* however, this results in a NPE. Placing the super rule in the around
* still results in a NPE.
*/
#Rule public TestSuccess _gebTestSuccesswatcher = new TestSuccess();
// I never see this called
void report() {
System.out.println("Custom Reporting Spec: " + _gebTestSuccesswatcher + "\t")
super.report()
}
}
I have also attempted to set this up in a custom reporter:
public CustomReporter extends ScreenshotAndPageSourceReporter implements Reporter {
#Rule
public TestSuccess _gebTestSuccesswatcher = new TestSuccess();
#Override
public void writeReport(Browser browser, String label, File outputDir) {
System.out.println("Custom Reporter: " + _gebTestSuccesswatcher);
super.writeReport(browser, label, outputDir)
}
}
However, regardless of whether or not my test fails, the success method on the watcher seems to be called. Here is my sample test:
class OneOff extends CustomReportingSpec {
def "Check One off"() {
when:
go "http://www.google.com"
then:
1 == 2
}
}
And the output:
Custom Reporter: null success: <false>.
Test Success [succeeded] Check One off(OneOff)
As you can see the success method is called upon completion of this failing test. If I modify the test to pass (i.e. 1 == 1), here is my output:
Custom Reporter: null success: <false>.
Test Success [succeeded] Check One off(OneOff)
Is there any way for me to get this Rule to work properly in the Custom Reporter? Or is there a way to get the browser instance in an extension? I've followed this guide to create a custom annotation and listener, but I can't access the Browser object. I have attempted adding an #Shared onto the declaration of the browser, but it isn't pulling the one in the Geb Configuration.

Your TestSuccess class doesn't work correctly due to a known limitation in Spock's TestRule support. Due to subtle differences between Spock's and JUnit's test execution model, calling base.evaluate() from a TestRule will not throw an exception in Spock, even if the test has failed. In many cases this won't make a difference, but for TestWatcher it will.
This is the only known limitation in Spock's rule support, and hopefully we'll find a way to overcome it at some point. There is no such semantic mismatch when using MethodRule.
If you want to implement your requirement with the help of a JUnit rule (which I think is fine), MethodRule is probably the better choice anyway. In contrary to TestRule, MethodRule provides access to the test instance, which will allow you to grab the session ID with browser.driver.sessionId.

Related

ArchUnit: Verify method only calls one outside method

In a Controller-Service-Datalayer architecture, I'm searching for a way to verify that my controller methods perform exactly one call to the service layer like this:
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteBlubber(#PathVariable("id") long blubberId) {
service.deleteBlubber(blubberId);
return new ResponseEntity<>("ok", HttpStatus.OK);
}
This should not be allowed:
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteBlubber(#PathVariable("id") long blubberId) {
service.deleteOtherStuffFirst(); // Opens first transaction
service.deleteBlubber(blubberId); // Opens second transaction - DANGER!
return new ResponseEntity<>("ok", HttpStatus.OK);
}
As you can see from the comments, the reason for this is to make sure that each request is handled in one transaction (that is started in the service layer), not multiple transactions.
It seems that ArchUnit can only check meta-data from classes and methods and not what's actually going on in a method. I would have to be able to count the request to the service classes, which seems to not be possible in ArchUnit.
Any idea if this might be possible? Thanks!
With JavaMethod.getMethodCallsFromSelf() you have access to all methods calls of a given method. This could be used inside a custom ArchCondition like this:
methods()
.that().areDeclaredInClassesThat().areAnnotatedWith(Controller.class)
.should(new ArchCondition<JavaMethod>("call exactly one service method") {
#Override
public void check(JavaMethod item, ConditionEvents events) {
List<JavaMethodCall> serviceCalls = item.getMethodCallsFromSelf().stream()
.filter(call -> call.getTargetOwner().isAnnotatedWith(Service.class))
.toList();
if (serviceCalls.size() != 1) {
String message = serviceCalls.stream().map(JavaMethodCall::getDescription).collect(joining(" and "));
events.add(SimpleConditionEvent.violated(item, message));
}
}
})

Wiremock and Spring Integration

I have a flow in Spring integration, which works as follows
Read from MQ--> Do a Message Transformation-->Send the transformed message to an Api
Now in my integration test, I am trying to mock the Api out and I tried 2 flavours but did not succeed.
Here is the test with Wiremock flavour (also posted on Wiremock Github)
String requestBody = TestUtil.createSampleInputMessage(rNumber);
System.out.println("Expected Post msg by EvApiClient :" + requestBody);
stubFor(post(urlEqualTo(TestUtil.EVENT_URL))
//.withHeader("Content-Type", equalTo("application/json"))
//.withRequestBody(equalToJson(requestBody))
);
ResponseEntity response = restTemplate.getForEntity("http://localhost:9966/__admin", String.class);
System.out.println("Printing Expected response :" + response);
sendToJmsOutChannel.send(MessageBuilder.withPayload(TestUtil.createSampleInputMessage(receiptNumber)).build());
verify(postRequestedFor(urlEqualTo(TestUtil.EVENT_URL)));`
The failure I get is
com.github.tomakehurst.wiremock.client.VerificationException: A request was unmatched by any stub mapping. Closest stub mapping was: expected:<
POST
/pa/his/v1/ev> but was:<
POST
/pa/his/v1/ev>
Here is the test with Spring's MockRestServiceServer
#Before
public void setup() throws PdsListenerException, URISyntaxException {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void test_001_success() throws InterruptedException, JSONException, URISyntaxException {
rNumber = TestUtil.generateRNumber();
String requestBody = TestUtil.createSampleInputMessage(rNumber);
mockServer.expect(once(), requestTo("http://localhost:9966" + TestUtil.EVENT_URL))
.andRespond(withCreatedEntity(new URI(TestUtil.EVENT_URL)));
sendToJmsOutChannel.send(MessageBuilder.withPayload(TestUtil.createSampleInputMessage(rNumber)).build());
mockServer.verify();
}
The failure I get is
java.lang.AssertionError: Further request(s) expected leaving 1 unsatisfied expectation(s).
0 request(s) executed.
I am not sure why the matching is failing. Is there anyone that can point out where I should be looking at.
Actually found out what the problem was with WireMock, and I will be using this flavour.
What I needed to change is
#ClassRule
public static WireMockClassRule wireMockRule = new WireMockClassRule(9966);
#Rule
public WireMockClassRule instanceRule = wireMockRule;
...
instanceRule.stubFor(post(urlPathEqualTo(Constants.EVENT_URL))
...
instanceRule.verify(1, postRequestedFor(urlEqualTo(TestUtil.EVENT_URL)));
Basically use the instance of the WireMockRule for stubbing and verifying, and not as I was doing before.
stubFor(post(urlEqualTo(TestUtil.EVENT_URL))

SessionAsSigner & restoreWrappedDocument()

Faced with the following issue: I am actively use DominoDocument class (wrapped Document) in my projects, particularly as basis for my business model objects.
Very often I have a need to access / iterate my business model objects as Anonymous user thus underlying lotus.domino.Document retrieved based on SessionAsSigner session object (for example in case of some REST Services, or in case of xAgent, etc).
The behavior of restoreWrappedDocument() method in such cases really breaks all flexibility of using such architecture: this method tries to restore wrapped document based on current execution environment access rights, and of course that causes errors with ACL.
Let’s consider the following code snippet as example:
public void test3() {
try {
System.out.println(">>>>>");
System.out.println(">>>>> START");
lotus.domino.Database db = AppBean.getSessionAsSigner().getDatabase(AppBean.getInstance().getContactsDBserverName(), AppBean.getInstance().getContactsDBname(), false);
Document notesDoc = db.getAllDocuments().getFirstDocument();
String dbName = notesDoc.getParentDatabase().getServer() + "!!" + notesDoc.getParentDatabase().getFilePath();
DominoDocument ds = DominoDocument.wrap(dbName, notesDoc, null, "exception", false, "UseWeb", null);
System.out.println(">> 1 >> " + ds.getValue("form"));
ds.getDocument().recycle();
try {
ds.restoreWrappedDocument();
}catch(Throwable e2){
System.out.println(">> 2 - exception - >> " + e2.toString());
e2.printStackTrace();
}
try {
System.out.println(">> 3 >> " + ds.getValue("form"));
}catch(Throwable e3){
System.out.println(">> 3 - exception - >> " + e3.toString());
}
System.out.println(">>>>> END");
System.out.println(">>>>>");
}catch(Exception e){
e.printStackTrace();
}
}
1) Scenario 1: executing this code by authenticated user that has access to target DB gives the following result:
So method works as expected and everything perfect.
2) Scenario 2: executing this code by Anonymous user causes Exception (generally, what is expected):
You can clearly see that restoreWrappedDocument() executes some helper methods in order to get DB, and of course that is done with current user access level (Anonymous).
Possible solutions:
The obvious solution is to add custom logic to my business object model, which will perform custom restore (basically based on Server&DB names and document UNID or NoteID).
What I am very curious whether there is any more smart or built-in method exist for restoring wrapped documents with SessionAsSigner rights?
Thanks!
I don't think there's a proper way to do this, other than your option 1, for better or for worse.
However, and I'm not saying this is a good idea, it seems like DominoDocument likely gets to its session through the current request map. If you want to be tricky, you could try temporarily swapping session out for sessionAsSigner in the request scope, calling restoreWrappedDocument, and then swapping it back.
A solution with a Helper class using Java Reflection:
(Incomplete, missing some parts)
package ch.hasselba.xpages;
import java.lang.reflect.Field;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.model.domino.wrapped.DominoDocument;
public class DominoDocumentUtil {
private static final long serialVersionUID = 1L;
private transient final Field wrappedObj;
private transient final DominoDocument dominoDoc;
public DominoDocumentUtil(DominoDocument doc) throws SecurityException,
NoSuchFieldException {
dominoDoc = doc;
wrappedObj= doc.getClass().getDeclaredField("_wrappedObject");
wrappedObj.setAccessible(true);
}
public void restoreWrappedDocument(Database db)
throws IllegalArgumentException, IllegalAccessException {
try {
Document doc = DominoUtils.getDocumentById(db, dominoDoc
.getDocumentId(), dominoDoc.isAllowDeletedDocs());
this.wrappedObj.set(dominoDoc, doc);
} catch (NotesException ne) {
throw new FacesExceptionEx(ne.getMessage());
}
}
}
To use the class you can call the restoreWrappedDocument method with a database opened with sessionAsSigner:
DominoDocumentUtil util = new DominoDocumentUtil(ds);
util.restoreWrappedDocument(db);

MSTest Unit Test - handling exceptions

I have a C# unit test using Selenium WebDriver to test to see if a link exists. Here's the code:
[TestMethod()]
public void RegisterLinkExistTest()
{
IWebElement registerLink = genericBrowserDriver.FindElement(By.PartialLinkText ("Register1"));
Assert.AreEqual("Register here", registerLink.Text, "Failed");
}
I wanted to see what happens if I set the PartialLinkText as "Register1" instead of "Register". MSTest failed this test with a exception thrown from Selenium. I wanted the Assert.AreEqual to execute but MSTest throws a exception on the previous line. I know I can use ExpectedException attribute to specify "OpenQA.Selenium.NoSuchElementException" but I don't want to do that way because I'm not expecting that exception. How do I go about handling this?
As #AD.Net already said, your test is working as expected.
You could catch the exception in case the link was not found but I don't see the point to do that. If the link is not found then the registerLink will be null. What's the point of asserting on a null object's property?
Your test works fine, just delete the Assert line.
However, if you also want to test the link's text try the following code:
[TestMethod()]
public void RegisterLinkExistTest()
{
try
{
IWebElement registerLink = genericBrowserDriver.FindElement(By.PartialLinkText ("Register1"));
Assert.AreEqual("Register here", registerLink.Text, "Register's link text mismatch");
}
catch(NoSuchElementException)
{
Assert.Fail("The register link was not found");
}
}
EDIT
You can seperate your test, the first test will check if the link exists and the second will assert it's properties.
[TestMethod()]
public void RegisterLinkExistTest()
{
IWebElement registerLink = genericBrowserDriver.FindElement(By.PartialLinkText ("Register1"));
}
[TestMethod()]
public void RegisterLinkTextTest()
{
IWebElement registerLink = genericBrowserDriver.FindElement(By.PartialLinkText ("Register1"));
Assert.AreEqual("Register here", registerLink.Text, "Register's link text mismatch");
}
Then use an OrderedTest and add them in that order so the RegisterLinkExistTest will be executed first. If it fails then the second test will not run.

first steps with FakeItEasy and problems with Action type

I have the following (here simplified) code which I want to test with FakeItEasy.
public class ActionExecutor : IActionExecutor
{
public void TransactionalExecutionOf(Action action)
{
try
{
// ...
action();
// ...
}
catch
{
// ...
Rollback();
}
}
public void Commit()
{ }
public void Rollback()
{ }
}
public class Service : IService
{
private readonly IRepository _repository;
private readonly IActionExecutor _actionExecutor;
// ctor for CI
public void ServiceMethod(string name)
{
_actionExecutor.TransactionalExecutionOf(() =>
{
var item = _repository.FindByName(ItemSpecs.FindByNameSpec(name));
if (item == null) throw new ServiceException("Item not found");
item.DoSomething();
_actionExecutor.Commit();
}
}
}
I want to test that the ServiceException is thrown so i setup my test like that
var repo = A.Fake<IRepository>();
A.CallTo(() => repo.FindByName(A<ISpec<Item>>.Ignored))
.Returns(null);
var executor = A.Fake<IActionExecutor>();
executor.Configure()
.CallsTo(x => x.Rollback()).DoesNothing();
executor.Configure()
.CallsTo(x => x.Commit()).DoesNothing();
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
With the following code
var service = new Service(executor, repo);
service.ServiceMethod("notExists")
.Throws(new ServiceException());
I get the following message
The current proxy generator can not intercept the specified method
for the following reason:
- Sealed methods can not be intercepted.
If I call the method directly on the service like
var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
I get this message
This is a DynamicProxy2 error: The interceptor attempted to 'Proceed'
for method 'Void TransactionalExecutionOf(System.Action)' which has no
target. When calling method without target there is no implementation
to 'proceed' to and it is the responsibility of the interceptor to
mimic the implementation (set return value, out arguments etc)
Now I am a bit confused and don't know what to do next.
Problems comes from the way you create fake and what you later expect it to do:
var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
What base method? FakeItEasy has no idea what the base class is, and hence the DynamicProxy2 exception in your second case. You can create partial mock this way:
var executor = A.Fake<ActionExecutor>();
Note that we're basing on actual implementation, not interface anymore
This however introduces a new set of problems, as methods on ActionExecutor are not virtual and therefore interceptor cannot hook up to well - intercept them. To make your current setup work, you'll have to change your ActionExecutor and make (all) the methods virtual.
However, you may (or even should) want to avoid modifications of existing code (which sometimes might not even be an option). You could then set up your IActionExecutor fake like this:
var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
.Invokes(f => new ActionExecutor()
.TransactionalExecutionOf((Action)f.Arguments.First())
);
This will allow you to work on faked object, with the exception of call to TransactionalExecutionOf which will be redirected to actual implementation.

Resources