Saturday 22 July 2023

Send latest cucumber report as email notification after automation execution

 Hi, In this blog post, we will see the core concept that worked for us to send cucumber report or extent report as notification after running automation. 

Normally, sending email is done through an SMTP server. This requires username and password details to be provided in the code. To avoid security threats or credentials exposure in the codebase, use perl MIME:Lite ( that is old and not recommended rather there are many alternative utilities such as Email::MIME or MIME::Entity and Email::Sender) utility that doesn't require any credentials( at least password is not required).

In this example, in order to send extent or cucumber report as automation notification, we use linux machine where perl is running and automation is run through a windows machine. 

In windows, cucumber or extent report will be generated --> copy this report to linux  and then perl will take care of sending email with html report as an attachment. 

Copying the jar file to linux, running perl are the two major things that the seleniuum/java code should take care of. And below is how it done. 

Read this article on how basic perl script can be used to send an html file(in this case the html file is cucumber report or extent report) as an attachment.

  • Add jsch dependency to pom.xml 
  • Write email code in AfterAll hook so always latest report is copied to linux. 
  • The important part is to write email code in jvm shutdown method that is inside the AfterAll hook 
  • Create a single session and open two channels, one channel is to copy and the other is to execute perl script that sits in linux. 

jsch dependency

<dependency>
	<groupId>com.jcraft</groupId>
	<artifactId>jsch</artifactId>
	<version>0.1.54</version>
</dependency>

Hooks.java
package com.email.common;
import java.net.InetAddress;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;

public class Hooks {

	final public static SftpProgressMonitor monitor = new SftpProgressMonitor() {
		public void init(final int op, final String source, final String target, final long max) {
			System.out.println("sftp start uploading file from:" + source + " to:" + target);
		}

		public boolean count(final long count) {
			System.out.println("sftp sending bytes: " + count);
			return true;
		}

		public void end() {
			System.out.println("sftp uploading is done.");
		}
	};

	@AfterAll(order = 2) // Cucumber AfterAll hook with order 2
	public static void putFile() throws JSchException, SftpException, IOException, InterruptedException {
		System.out.println("AfterAll - with order=2");

		InetAddress localhost = InetAddress.getLocalHost();
		System.out.println("System IP Address : " + (localhost.getHostAddress()).trim());

		if (localhost.getHostAddress().trim().equals("127.0.0.1")) {
			Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
				@Override
				public void run() {
					String FromWindows = "C:\\Automation\\target\\cucumber-reports\\cucumber-report.html";
					String ToLinux = "/home/linuxadmin/AutomationProject/cucumber-report.html";

					JSch jsch = new JSch();
					Session session = null;
					try {
						session = jsch.getSession("linuxadmin", "12.445.1.999", 22); //linux machine
						session.setConfig("StrictHostKeyChecking", "no");
						session.setPassword("MyPassword+@!");
						session.connect();
						Channel channel = session.openChannel("sftp");
						channel.connect();
						ChannelSftp sftpChannel = (ChannelSftp) channel;
						System.out.println("Server's home directory: " + sftpChannel.getHome());
						try {
							sftpChannel.put(FromWindows, ToLinux, monitor, ChannelSftp.OVERWRITE);
						} catch (SftpException e) {
							System.out.println("File is not found in Windows: ");
							e.printStackTrace();
						}

						Channel channel2 = session.openChannel("exec");
						((ChannelExec) channel2).setPty(true);
						((ChannelExec) channel2).setCommand("perl /home/linuxadmin/AutomationProject/report-notify-email.pl");
						channel.setInputStream(null);
						((ChannelExec) channel2).setErrStream(System.err);

						InputStream in = channel2.getInputStream();
						channel2.connect();
						byte[] tmp = new byte[1024];
						while (true) {
							while (in.available() > 0) {
								int i = in.read(tmp, 0, 1024);
								if (i < 0)
									break;
								System.out.print(new String(tmp, 0, i));
							}
							if (channel2.isClosed()) {
								System.out.println("exit-status: " + channel2.getExitStatus());
								break;
							}
							try {
								Thread.sleep(1000);
							} catch (Exception ee) {
							}
						}

						sftpChannel.exit();
						channel.disconnect();
						channel2.disconnect();
						session.disconnect();

					} catch (Exception e) {
						e.printStackTrace();
					}

					System.out.println("Shutdown Hook is running !");
				}
			}));
			System.out.println("Selenium application is terminating ...");

		} else {
			System.out.println("Skipping sending email from linux server");
		}

	}

}


Below code is normally works well but the backdrop is one must provide an email account credentials (username and password). This exposes security issues so to avoid follow aforementioned technique. 

javax.mail dependency
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
	<groupId>javax.mail</groupId>
	<artifactId>mail</artifactId>
	<version>1.4.7</version>
</dependency>


@AfterAll(order = 1) // Cucumber AfterAll hook with order 1
	public static void afterAllEmail() {

		 final String username = "sadakar@email.com";
		 final String password = "MyPassword#12!";
		 		 
		 Properties props = new Properties(); 
		 
		 props.put("mail.smtp.auth", true);
		 props.put("mail.smtp.starttls.enable", true);
		 props.put("mail.smtp.starttls.required", true);
		 props.put("mail.smtp.host","smtp.office365.com");
		 props.put("mail.smtp.ssl.trust", "smtp.office365.com");
		 props.put("mail.smtp.port","587"); 
		 props.put("mail.debug", "true");
		 props.put("mail.smtp.ssl.protocols", "TLSv1.2");
		 
		 Session session = Session.getInstance(props, new javax.mail.Authenticator() {
		 protected PasswordAuthentication getPasswordAuthentication() { return new
		 PasswordAuthentication(username, password); } });
		 
		 try {
		 
			 Message message = new MimeMessage(session); 
			 message.setFrom(new InternetAddress("sadakar@email.com"));
			 message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("toemail@email.com"));
			 message.setSubject("UI Automation Report");
			 message.setText("Please refer to the AutomationExtentOrSparkReport.html for the test results");
			 
			 MimeBodyPart messageBodyPart = new MimeBodyPart();
			 Multipart multipart = new MimeMultipart();
			 
			 messageBodyPart = new MimeBodyPart(); String automationReportPath =
			 System.getProperty("user.dir");
			 System.out.println("Spark Report File Path="+automationReportPath); 
			 String file = automationReportPath+"//target//ExtentReport//AutomationExtentOrSparkReport.html";
			 String fileName = "AutomationExtentOrSparkReport.html"; 
			 DataSource source = new FileDataSource(file); 
			 messageBodyPart.setDataHandler(new DataHandler(source)); 
			 messageBodyPart.setFileName(fileName);
			 
			 multipart.addBodyPart(messageBodyPart);
			 message.setContent(multipart);
			 
			 System.out.println("Sending");
			 Transport.send(message);
			 System.out.println("Done");
		 
		 } catch (MessagingException e) { e.printStackTrace(); }
			System.out.println("Email hook ending");
		 
	}

I hope you find this useful! If you like it, pls do subscribe my YouTube channel for automation or tech updates.

No comments:

Post a Comment