是的我蛋疼,因为最近要用 ExtentReports 生成测试报告,但是我 E 文不好,所以边翻译别学习,有些翻译的我觉得没有表达原文含义的或者可能会有二义性的地方,我都把原文贴出来了,反正将就着看吧,啊哈哈哈哈。不说废话上正文。

extentReports 基本用法 Basic Usage


初始化 Initializing Report

为了成功生成测试信息,需要启动并建立 Reporter(测试报告相关信息)和 ExtentReports 的关联。如果启动 Reporter 失败或未关联至 ExtentReports 类,在创建测试用例或将测试执行结果上传时,将提示 IllegalStateException 错误。
初始化 Reporter 示例:

// 初始化HtmlReporter
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html"); 

// 初始化ExtentXReporter
ExtentXReporter extentxReporter = new ExtentXReporter("mongodb-host", mongodb-port);

// 初始化EmailReporter (社区版不支持)
ExtentEmailReporter emailReporter = new ExtentEmailReporter("email.html");

// 初始化realtime logger (社区版不支持)
ExtentLogger logger = new ExtentLogger();

// 创建ExtentReports对象 
ExtentReports extent = new ExtentReports();

// 将HtmlReporter关联ExtentReports对象
extent.attachReporter(htmlReporter);

// 将所有Reporter都关联ExtentReports对象
extent.attachReporter(htmlReporter, extentxReporter, emailReporter, logger);

生成报告

当测试执行完成并准备将测试执行结果生成测试报告时,只需要简单调用 flush() 方法。

以下是几种报告模式在调用 flush() 方法生成/更新执行结果时执行的操作:

HtmlReporter: 离线报告(社区版不支持)

HtmlReporter 允许创建离线报告(将 css 和 js 文件本地保存),使用以下方法:

ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setCreateOfflineReport(true);

追加测试结果

ExtentHtmlReporter 和 ExtentXReporter 允许在已有报告中追加新的测试结果,设置以下属性:

在 HtmlReporter 中追加测试结果

htmlReporter.setAppendExisting(true);

在 ExtentXReporter 中追加测试结果

如果希望在同一 reportId 的已有测试报告中追加新的测试结果,则:

extentXReporter.setAppendExisting(true, reportId);

// or:
extentxReporter.config().setReportObjectId(ObjectId id);
extentxReporter.config().setReportObjectId(String id);

如果希望在同一 reportName 的已有测试报告中追加新的测试结果,则:

extentxReporter.setAppendExisting(true);

注意:如果 reportName 非唯一,将在最后生成的测试报告中追加测试结果。

创建测试用例(通过 Extent 进行测试)

使用 createTest 或 createNode 方法来建立测试用例:

注意:只可能在至少一个 reporter 关联至 ExtentReports 时,才能创建 tests 或 nodes

代码示例:

// creating tests
ExtentTest test = extent.createTest("MyFirstTest");
test.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .pass("details");

// test with description
extent
    .createTest("MyFirstTest", "Test Description")
    .pass("details");

增加子案例

使用 createNode 方法将测试 node 作为其他 test 的子案例。

代码示例:

// creating nodes   
ExtentTest parentTest = extent.createTest("MyFirstTest");
ExtentTest childTest = parentTest.createNode("MyFirstChildTest");
childTest.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .createNode("MyFirstChildTest")
    .pass("details");

// node description
ExtentTest childTest = parentTest.createNode("MyFirstChildTest", "Node Description");

创建 BDD 模式的测试用例

用 gherkin 模型或模型名来创建 cucumber / gherkin 模式的测试用例:

注意:当创建 BDD 模式的测试用例时,所有的测试用例必须用相同的样式。不要在 BDD 中混用常规测试案例样式。一个测试报告中只能包含一种测试用例样式。

代码示例:

// gherkin classes

// feature
ExtentTest feature = extent.createTest("Refund item");

// scenario
ExtentTest scenario = feature.createNode(Scenario.class, "Jeff returns a faulty microwave");
scenario.createNode(Given.class, "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(And.class, "he has a receipt").pass("pass");
scenario.createNode(When.class, "he returns the microwave").pass("pass");
scenario.createNode(Then.class, "Jeff should be refunded $100").fail("fail");

// using keyword names

// feature
ExtentTest feature = extent.createTest("Refund item");

// scenario
ExtentTest scenario = feature.createNode(new GherkinKeyword("Scenario") , "Jeff returns a faulty microwave");
scenario.createNode(new GherkinKeyword("Given"), "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(new GherkinKeyword("And"), "he has a receipt").pass("pass");
scenario.createNode(new GherkinKeyword("When"), "he returns the microwave").pass("pass");
scenario.createNode(new GherkinKeyword("Then"), "Jeff should be refunded $100").fail("fail");

移除测试用例

如果希望移除任何已经开始执行的测试用例,调用 removeTest(3.0.4+)方法:

ExtentTest test = extent.createTest("Test");
extent.removeTest(test);

修改测试执行结果显示顺序(社区版不支持)

如果希望设置测试案例在测试报告中的显示位置,则:

// The 1st executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.OLDEST_FIRST);

// The last executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.NEWEST_FIRST);

创建事件记录

ExtentTest test = extent.createTest("TestName");
test.log(Status.PASS, "pass");
// or:
test.pass("pass");

test.log(Status.FAIL, "fail");
// or:
test.fail("fail");

记录异常

To log exceptions, simply pass the exception.

Note: doing this will also enable the defect/bug tab in the report.

Exception e;
test.fail(e);

建立测试案例分类

可以使用 assignCategory 方法来建立测试案例分类:

test.assignCategory("Regression");
test.assignCategory("Regression", "ExtentAPI");
test.assignCategory("Regression", "ExtentAPI", "category-3", "cagegory-4", ..);

// while creating test
extent
    .createTest("MyFirstTest")
    .assignCategory("Regression")
    .pass("details");

建立测试案例作者信息

可以使用 assignAuthor 方法来创建测试案例作者信息

test.assignAuthor("aventstack");
test.assignAuthor("name1", "name2");

// while creating test
extent
    .createTest("MyFirstTest")
    .assignAuthor("aventstack")
    .pass("details");

截屏

可以在测试案例和日志中关联截屏:

// adding screenshots to log
test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());

// or:
MediaModelProvider mediaModel = MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build();
test.fail("details", mediaModel);   

// adding screenshots to test
test.fail("details").addScreenCaptureFromPath("screenshot.png");

Base64 截屏

使用 createScreenCaptureFromBase64String 或 addScreenCaptureFromBase64String 方法在测试案例和日志中关联 Base64 截屏。

// adding base64 strings to log:
test.warning("details", MediaEntityBuilder.createScreenCaptureFromBase64String("base64String").build());
test.log(Status.WARNING, "details", MediaEntityBuilder.createScreenCaptureFromBase64String("base64String").build());

// adding base64 strings to tests:
test.addScreenCaptureFromBase64String("base64String");

自定义 HTML 样式

使用 HTML 标签进行 HTML 报告样式自定义:

extent.log(LogStatus.INFO, "HTML", "Usage: BOLD TEXT");

在测试报告中增加系统信息

通过使用 setSystemInfo 方法可以在测试报告中增加系统或环境信息。
注意:方法将自动增加信息到所有启动的 reporter 中

extent.setSystemInfo("os", "win7");

ExtentHtmlReporter 快捷键 Shortcuts

ExtentHtmlReporter 支持一些快捷键,用来快速在视图和导航中切换。

视图 Views

t - test-view
c - category-view
x - exception-view
d - dashboard

过滤器 Filters

p - show passed tests
e - show error tests
f - show failed tests
s - show skipped tests
w - show warning tests
esc - clear filters

上下翻屏 Scroll

down-arrow - scroll down
up-arrow - scroll up

ExtentHtmlReporter 设置 Features

Extent-Config.xml 配置说明

专业版 Professional

<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>

        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>

        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>

        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>

        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>

        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>

        <!-- settings to enable/disable views -->
        <!-- professional version only -->
        <enableCategoryView>true</enableCategoryView>
        <enableAuthorView>false</enableAuthorView>
        <enableExceptionView>true</enableExceptionView>
        <enableTestRunnerLogsView>true</enableTestRunnerLogsView>

        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {

                });
            ]]>
        </scripts>

        <!-- custom styles -->
        <styles>
            <![CDATA[

            ]]>
        </styles>
    </configuration>
</extentreports>

社区版 Community

<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>

        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>

        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>

        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>

        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>

        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>

        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {

                });
            ]]>
        </scripts>

        <!-- custom styles -->
        <styles>
            <![CDATA[

            ]]>
        </styles>
    </configuration>
</extentreports>

自动截屏管理(社区版不支持)Automatic Screenshot Management

使用如下设置来自动创建截屏相对地址与测试报告的关联:

htmlReporter.config().setAutoCreateRelativePathMedia(true);

这个设置能将文件拷贝到对应相对路径的目录下,而不需要手动设置:

代码示例:

ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setAutoCreateRelativePathMedia(true);

test1.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("1.png").build());
test2.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("2.png").build());

上面两个截屏将自动保存并关联至测试报告。你可以移动报告和截屏而不会有任何副作用,比如破坏测试报告或者加载截屏失败错误。

离线报告(社区版不支持)

htmlReporter.config().setCreateOfflineReport(true);

在现有报告中追加测试案例执行结果

ExtentHtmlReporter htmlreporter = new ExtentHtmlReporter("Extent.html");
htmlreporter.setAppendExisting(true);

配置分析策略 Configuring Analysis Strategy

可以在测试报告中配置分析策略(只适用于 HTMLReporter)。在下面链接中查看不同的地方:

配置视图可见性(社区版不支持)Configuring View Visibility

可以使用 config.xml 或者代码来配置视图可见性:

代码示例:

// enable category view
htmlReporter.config().setCategoryViewVisibility(true);

// disable Author view
htmlReporter.config().setAuthorViewVisibility(false);

// disable TestRunnerLogs view
htmlReporter.config().setTestRunnerLogsViewVisibility(false);

// enable Exception view
htmlReporter.config().setExceptionViewVisibility(true);

config.xml 配置示例:

<!-- settings to enable/disable views -->
<enableCategoryView>true</enableCategoryView>
<enableAuthorView>false</enableAuthorView>
<enableExceptionView>true</enableExceptionView>
<enableTestRunnerLogsView>true</enableTestRunnerLogsView>
htmlReporter.loadXMLConfig("extent-config.xml");

建立自定义 Dashboard 部件(社区版不支持)Creating Custom Dashboard Sections

使用 htmlReporter.views().dashboard().setSection(...) 在报告的 Dashboard 中增加自定义部件:

代码示例:

// setSection(String name, SectionSize size, String[] header, List data)
String[] header = new String[] { "HeaderA", "HeaderB", "HeaderC" };

List list = new ArrayList();
list.add(new String[] { "cell1", "cell2", "cell3" });
list.add(new String[] { "cell4", "cell5", "cell6" });

htmlReporter.views().dashboard().setSection("Sample", SectionSize.S4, header, list);

ExtentXReporter 设置 Features

获取 ReportID

extentXReporter.getReportId();

获取 ProjectID

extentXReporter.getProjectId();

在已有报告中追加测试结果

extentXReporter.setAppendExisting(true, reportId);

// or:
extentXReporter.config().setReportObjectId(id);

追加测试结果到最后一次执行结果(社区版不支持)

(pro version only, 3.0.2+) 可以在 ExtentX(mongodb)中,将当前的执行结果追加到上一次执行结果中。

代码示例:

extentXReporter.appendToLastRunReport();

// or:
ObjectId reportId = extentXReporter.getLastRunReportId();
extentXReporter.setAppendExisting(true, reportId);

当有多个项目时小心使用该设置。假如测试代码中指定项目失败或在该设置之后才指定项目,ExtentXReporter 将默认将测试结果追加到可能属于其他项目的测试报告中。切记在使用该设置时设置项目(当你有多个项目时)

//设置项目名称
extentXReporter.config().setProjectName(projectName);

标记助手 Makeup Helpers

测试案例中允许使用一些标记协助定位:

代码块 Code Block

String code = "\n\t\n\t\tText\n\t\n";
Markup m = MarkupHelper.createCodeBlock(code);

test.pass(m);
// or
test.log(Status.PASS, m);

标签 Label

String text = "extent";
Markup m = MarkupHelper.createLabel(text, ExtentColor.BLUE);

test.pass(m);
// or
test.log(Status.PASS, m);

卡片 Card

String text = "This text will become part of a material card.";
Markup m = MarkupHelper.createCard(text, ExtentColor.CYAN);

test.pass(m);
// or
test.log(Status.PASS, m);
String url = "http://extentreports.com";
String name = "extent";
Markup m = MarkupHelper.createExternalLink(url, name);

test.pass(m);
// or
test.log(Status.PASS, m);
test.info(MarkupHelper.createModal("Modal text"));

设置 Configuration

重写测试结果类型 Status Hierarchy Override Configuration

通过 ExtentReports config() 可以自定义测试结果类型:

List statusHierarchy = Arrays.asList(
                    Status.FATAL,
                    Status.FAIL,
                    Status.ERROR,
                    Status.WARNING,
                    Status.SKIP,
                    Status.PASS,
                    Status.DEBUG,
                    Status.INFO
);

extent.config().statusConfigurator().setStatusHierarchy(statusHierarchy);

ExtentHtmlReporter 设置 Configuration

每个 reporter 都支持许多设置项,用来改变外观,增加给荣,管理测试等。

// make the charts visible on report open
htmlReporter.config().setChartVisibilityOnOpen(true);

// create offline report (pro-only)
htmlReporter.config().setCreateOfflineReport(true);

// automatic screenshot management (pro-only)
htmlReporter.config().setAutoCreateRelativePathMedia(true);

// report title
htmlReporter.config().setDocumentTitle("aventstack - ExtentReports");

// encoding, default = UTF-8
htmlReporter.config().setEncoding("UTF-8");

// protocol (http, https)
htmlReporter.config().setProtocol(Protocol.HTTPS);

// report or build name
htmlReporter.config().setReportName("Build-1224");

// chart location - top, bottom
htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);

// theme - standard, dark
htmlReporter.config().setTheme(Theme.STANDARD);

// set timeStamp format
htmlReporter.config().setTimeStampFormat("mm/dd/yyyy hh:mm:ss a");

// add custom css
htmlreporter.config().setCSS("css-string");

// add custom javascript
htmlreporter.config().setJS("js-string");

ExtentXReporter 设置 Configuration

// project name
extentx.config().setProjectName("ProjectName");

// report or build name
extentx.config().setReportName("Build-1224");

// server URL
// ! must provide this to be able to upload snapshots
// Note: this is the address to the ExtentX server, not the Mongo database
extentx.config().setServerUrl("http://localhost:1337");

例子

基本用法

public class Main {
    public static void main(String[] args) {
        // start reporters
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");

        // create ExtentReports and attach reporter(s)
        ExtentReports extent = new ExtentReports();
        extent.attachReporter(htmlReporter);

        // creates a toggle for the given test, adds all log events under it    
        ExtentTest test = extent.createTest("MyFirstTest", "Sample description");

        // log(Status, details)
        test.log(Status.INFO, "This step shows usage of log(status, details)");

        // info(details)
        test.info("This step shows usage of info(details)");

        // log with snapshot
        test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());

        // test with snapshot
        test.addScreenCaptureFromPath("screenshot.png");

        // calling flush writes everything to the log file
        extent.flush();
    }
}

TestNG 例子

使用 ExtentTestNGReportBuilder

public class ExtentTestNGReportBuilder {

    private static ExtentReports extent;
    private static ThreadLocal parentTest = new ThreadLocal();
    private static ThreadLocal test = new ThreadLocal();

    @BeforeSuite
    public void beforeSuite() {
        extent = ExtentManager.createInstance("extent.html");
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
        extent.attachReporter(htmlReporter);
    }

    @BeforeClass
    public synchronized void beforeClass() {
        ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
    }

    @BeforeMethod
    public synchronized void beforeMethod(Method method) {
        ExtentTest child = parentTest.get().createNode(method.getName());
        test.set(child);
    }

    @AfterMethod
    public synchronized void afterMethod(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE)
            test.get().fail(result.getThrowable());
        else if (result.getStatus() == ITestResult.SKIP)
            test.get().skip(result.getThrowable());
        else
            test.get().pass("Test passed");

        extent.flush();
    }
}

public class ExtentManager {

    private static ExtentReports extent;

    public static ExtentReports getInstance() {
        if (extent == null)
            createInstance("test-output/extent.html");

        return extent;
    }

    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);

        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);

        return extent;
    }
}

使用 TestNG IReporter

public class ExtentTestNGIReporterListener implements IReporter {

    private static final String OUTPUT_FOLDER = "test-output/";
    private static final String FILE_NAME = "Extent.html";

    private ExtentReports extent;

    @Override
    public void generateReport(List xmlSuites, List suites, String outputDirectory) {
        init();

        for (ISuite suite : suites) {
            Map result = suite.getResults();

            for (ISuiteResult r : result.values()) {
                ITestContext context = r.getTestContext();

                buildTestNodes(context.getFailedTests(), Status.FAIL);
                buildTestNodes(context.getSkippedTests(), Status.SKIP);
                buildTestNodes(context.getPassedTests(), Status.PASS);

            }
        }

        for (String s : Reporter.getOutput()) {
            extent.setTestRunnerOutput(s);
        }

        extent.flush();
    }

    private void init() {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        htmlReporter.config().setDocumentTitle("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setReportName("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setTheme(Theme.STANDARD);

        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setReportUsesManualConfiguration(true);
    }

    private void buildTestNodes(IResultMap tests, Status status) {
        ExtentTest test;

        if (tests.size() > 0) {
            for (ITestResult result : tests.getAllResults()) {
                test = extent.createTest(result.getMethod().getMethodName());

                for (String group : result.getMethod().getGroups())
                    test.assignCategory(group);

                if (result.getThrowable() != null) {
                    test.log(status, result.getThrowable());
                }
                else {
                    test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                }

                test.getModel().setStartTime(getTime(result.getStartMillis()));
                test.getModel().setEndTime(getTime(result.getEndMillis()));
            }
        }
    }

    private Date getTime(long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();      
    }
}

使用 TestNG ITestListener

public class ExtentTestNGITestListener implements ITestListener {

    private static ExtentReports extent = ExtentManager.createInstance("extent.html");
    //private static ThreadLocal parentTest = new ThreadLocal();
    //private static ThreadLocal test = new ThreadLocal();
    //不造为嘛,代码苦手表示用上面的代码就是编译报错,所以就随便改了改,如下,就通过了。。
    private static ThreadLocal<ExtentTest> parentTest = new ThreadLocal();
    private static ThreadLocal<ExtentTest> test = new ThreadLocal();

    @Override
    public synchronized void onStart(ITestContext context) {
        ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
    }

    @Override
    public synchronized void onFinish(ITestContext context) {
        extent.flush();
    }

    @Override
    public synchronized void onTestStart(ITestResult result) {
        ExtentTest child = parentTest.get().createNode(result.getMethod().getMethodName());
        test.set(child);
    }

    @Override
    public synchronized void onTestSuccess(ITestResult result) {
        test.get().pass("Test passed");
    }

    @Override
    public synchronized void onTestFailure(ITestResult result) {
        test.get().fail(result.getThrowable());
    }

    @Override
    public synchronized void onTestSkipped(ITestResult result) {
        test.get().skip(result.getThrowable());
    }

    @Override
    public synchronized void onTestFailedButWithinSuccessPercentage(ITestResult result) {

    }
}

public class ExtentManager {

    private static ExtentReports extent;

    public static ExtentReports getInstance() {
        if (extent == null)
            createInstance("test-output/extent.html");

        return extent;
    }

    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);

        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);

        return extent;
    }
}

使用 JavaMail API 发送 Email

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Email {

    // supply your SMTP host name
    private static String host = "host";

    // supply a static sendTo list
    private static String to = "recipient@host.com";

    // supply the sender email
    private static String from = "sender@host.com";

    / default cc list
    private static String cc = "";

    // default bcc list
    private static String bcc = "";

    public static void send(String from, String to, String subject, String contents) throws MessagingException {        
        Properties prop = System.getProperties();
        prop.setProperty("mail.smtp.host", host);

        Session session = Session.getDefaultInstance(prop);

        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
        message.setSubject(subject);
        message.setContent(contents, "text/html");

        List toList = getAddress(to);
        for (String address : toList) {
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
        }

        List ccList = getAddress(cc);
        for (String address : ccList) {
            message.addRecipient(Message.RecipientType.CC, new InternetAddress(address));
        }

        List bccList = getAddress(bcc);
        for (String address : bccList) {
            message.addRecipient(Message.RecipientType.BCC, new InternetAddress(address));
        }

        Transport.send(message);
    }

    public static void send(String to, String subject, String contents) throws MessagingException {
        send(from, to, subject, contents);
    }

    public static void send(String subject, String contents) throws MessagingException {
        send(from, to, subject, contents);
    }

    private static List getAddress(String address) {
        List addressList = new ArrayList();

        if (address.isEmpty())
            return addressList;

        if (address.indexOf(";") > 0) {
            String[] addresses = address.split(";");

            for (String a : addresses) {
                addressList.add(a);
            }
        } else {
            addressList.add(address);
        }

        return addressList;
    }

}

// usage
Email.send("me@host.com", "recip1@host.com;recip2@host.com", "New Subject", "<h1>header</h2>")


↙↙↙阅读原文可查看相关链接,并与作者交流