One-step Tableau report test automation

One-step Tableau report test automation

Written by: Przemysław Jagodziński, Senior Software Test Engineer

JOIN ESPEO SOFTWARE

Check out our latest open job positions and apply!

Join Espeo Software

For the last couple of years the BI solutions like Tableau and PowerBI started to be very desirable for the projects. It did not take much time when business started to ask for test automation of created reports. Since reports can be published to the server like Tableau Server, the obvious choice was to use Selenium. Immediately when creating automated tests we can encounter problems validating reports. We can verify that a report has been generated but we cannot say whether it contains data or not. In this little tutorial I will present how to create a one-step test to retrieve data from a report using Tableau JavaScript API.

Cucumber Test

Let’s start with a simple cucumber scenario. It contains two steps. First one sets up a web driver and navigates users to the public Tableau report. Second one validates the report. We will simply verify if a provided in the parameter report Storm Map Sheet contains value ALEX.

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] @Test Scenario: Report contains data Given I navigate to ”https://public.tableau.com/views/RegionalSampleWorkbook/Storms” And I verify ”Storm Map Sheet” report is generated with data ”ALEX” [/dm_code_snippet]

Test Steps

Next step is a standard definition of test steps. Since step 1 is pretty obvious we will concentrate on step 2.

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] @Given(”I navigate to {string}”) public void iNavigateToUrl(String url) { urlAddress = url; tableauPage.navigateToReport(url); } [/dm_code_snippet]

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] @And(”I verify {string} report is generated with data {string}”) public void iVerifyReportIsGenerated(String reportName, String expectedData) { tableauPage.updateJSScript(urlAddress, reportName); tableauPage.verifyReportIsGenerated(expectedData); } [/dm_code_snippet]

We see two method calls. In the first one we will be updating the existing JS script with provided values reportName and url. In the second method we will be validating Tableau report data.

Methods Implementations

Update JS script method does following things:

  • take template.js and put it into string
  • update values of $url and $reportName with provided ones
  • write the result to script.js

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] public void updateJSScript(String url, String reportName) { String data = ””; data = Files.readString(Paths.get(”Path to template.js”)); data = data.replace(”$url”, ”\””+url+”\””); data = data.replace(”$reportName”, ”\””+reportName+”\””); Files.write( Paths.get(”Path to script.js”), data.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE); } [/dm_code_snippet]

Having updated script.js we can now navigate to index.html. When opening the page, the data of Tableau report are retrieved from Tableau JavaScript API and printed to the browser console. Then the method reads those data and validates them comparing with expectedData.

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] public void verifyReportIsGenerated(String expectedData) { driver.get(”file:\\\\”+”Path to index.html”) LogEntries log = driver.manage().logs().get(LogType.BROWSER); List logs = log.getAll(); for (LogEntry e : logs) { if (data != null) { if (e.toString().contains(”script.js”)) { Assert.assertTrue(e.toString().contains(expectedData)); } } } } [/dm_code_snippet]

Setting Chrome Driver properties

Important thing is to pass appropriate options to ChromeDriver in order to print ALL logs to the console. Obviously we can specify which logs should be visible in the console. In our case it is safe to print all. We then filter logs and validate just what is useful for us.

Unfortunately, this works only for the Chrome browser.

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] public static WebDriver setupChromeDriver() { ChromeOptions options = new ChromeOptions(); var logPrefs = new LoggingPreferences(); logPrefs.enable(LogType.BROWSER, Level.ALL); options.setCapability(CapabilityType.LOGGING_PREFS, logPrefs); options.setCapability(”goog:loggingPrefs”, logPrefs); options.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, true); driver = new ChromeDriver(options); return driver; } [/dm_code_snippet]

template.js

Finally template.js. Script contains $reportName and $url parameters which are replaced by test scenario data. In the function initViz() we call getData() function which gets underlying data from Tableau JavaScript API.

[dm_code_snippet background=”no” background-mobile=”no” slim=”no” bg-color=”#212725″ theme=”dark” language=”javascript” wrapped=”no” height=”400px” copy-text=”Get the Code!” copy-confirmed=”You have it!”] var viz, sheet, table; function initViz() { var containerDiv = document.getElementById(”vizContainer”), url = $url viz = new tableau.Viz(containerDiv, url, ””); setTimeout(getData, 4000) } function getData() { sheet = viz.getWorkbook().getActiveSheet().getWorksheets().get($reportName); options = { maxRows: 1, ignoreAliases: false, ignoreSelection: true, includeAllColumns: false }; sheet.getUnderlyingDataAsync(options).then(function(t)) { table = t; console.log(JSON.stringify(table.getData())); var tgt = document.getElementById(”dataTarget”); tgt.innerHTML = ”

Underlying Data:

” + JSON.stringify(table.getData()) + ”

”; }); } } [/dm_code_snippet]

index.html

And last thing is to create a simple html file to load underlying data when opening it.

<!DOCTYPE html>
<html>
<head>
   <title>Get Data</title>
   <script type="text/javascript" src="https://public.tableau.com/javascripts/api/tableau-2.min.js"></script>
   <script type="text/javascript" src="script.js"></script>
</head>
<body onload="initViz();">
</body>
</html>

Summary

As promised, a one-step test has been presented. As usual it took me much more time to figure out how to retrieve data from a Tableau report than to implement a simple scenario. This is not a fully UI test but definitely better than just simple selenium verification whether a report exists or not. Hopefully a similar solution will be soon available in different BI tools.

Do you like this article?
Tags
Share
How can we help you start your new digital project?
You can select more than one answer.
Are you looking for any particular skills?
You can select more than one answer.
Let us know, how we can reach you
* Required fields
Miten voimme auttaa digitaalisen projektin aloittamisessa?
Voit valita useamman vastausvaihtoehdon.
Etsitkö tiettyä taitoa?
Voit valita useamman vastausvaihtoehdon.
Kerro meille kuinka voimme tavoittaa teidät
* Pakolliset kentät