{"id":11573,"date":"2022-07-31T00:39:16","date_gmt":"2022-07-30T22:39:16","guid":{"rendered":"https:\/\/www.salvis.com\/blog\/?p=11573"},"modified":"2023-11-08T16:51:01","modified_gmt":"2023-11-08T15:51:01","slug":"testing-with-utplsql-made-easy-with-sql-developer","status":"publish","type":"post","link":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/","title":{"rendered":"Testing With utPLSQL &#8211; Made Easy With SQL Developer"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Nowadays, everything is about automation. Software testing is no exception. After an introduction, we will create and run utPLSQL tests with Oracle SQL Developer for a fictitious user story. Our tests will cover a PL\/SQL package, a virtual column, two database triggers and a view. The full code samples can be found <a href=\"https:\/\/github.com\/PhilippSalvisberg\/utplsql-red-stack-demo\">here<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This blog post is based on my German article <a href=\"https:\/\/backoffice.doag.org\/formes\/pubfiles\/14512922\/docs\/Publikationen\/Red-Stack-Magazin-inkl-Business-News\/2022\/04-2022\/04_2022-Red_Stack_Magazin-WEB.pdf#page=32\">Testen mit utPLSQL &#8211; Leicht gemacht mit SQL Developer<\/a>, published in issue 4\/2022 of the <a href=\"https:\/\/www.doag.org\/de\/mitmachen\/red-stack-business-news-archiv\/\" rel=\"nofollow\">Red Stack Magazin<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Test Automation?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the definition from the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Test_automation\">English Wikipedia page<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Test automation is the use of software separate from the software being tested to control the execution of tests and the comparison of actual outcomes with predicted outcomes.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">If the actual results match the expected results, then the tests are successful. An important aspect of test automation is the prior definition of expected results. Expected results are simply requirements. In other words, we use automated tests to check whether the requirements are met.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Do We Need Automated Tests?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The use of agile methods and the associated shorter release cycles mean that we need to test software more frequently. In a CI\/CD environment, automated tests can be executed directly after a commit in a version control system. Through this continuous testing, the state of the software quality is constantly available. In case of deviations, the possible causes can be identified more quickly due to the shorter intervals between tests. In addition, the changes, since the last successful test run, are still present in the minds of the developers. All this simplifies troubleshooting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When software is tested only semi-automatically or manually, this leads to higher costs, higher risks, delivery delays or quality problems. It is simply not efficient to perform automatable tasks manually.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Repeating automated tests is cheap. Unlike manual tests, there is no reason to compromise. This is especially important for central components of a software solution. We need to ensure that changes do not have any unwanted effects on other parts of the software solution.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Do Automated Tests Have Downsides?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the end, an automated test is also software that needs to be maintained. Tests that are not reliable are especially problematic. These are so-called &#8220;flaky&#8221; tests, which sometimes fail, but if you repeat them often enough, deliver the expected result. Such tests quickly do more harm than good. The more flaky tests there are, the higher the probability that the CI job will fail, resulting in manual activities.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Can We Test in the Database?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Basically, every component of an application installed in the database can be tested automatically. These are, for example, object types, packages, procedures, functions, triggers, views and constraints.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We do not write tests for ordinary not null check constraints since we do not want to test the basic functionality of a database. However, it might be useful to test a complex expression of a virtual column or a complex condition of a check constraint. A view is based on a <code>select<\/code> statement. <code>select<\/code> statements can be quite complex even without PL\/SQL units in the <code>with_clause<\/code>. Consequently, we also have the need to check whether views return the expected results.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Object types, packages, procedures, functions and triggers mainly contain code in PL\/SQL. However, PL\/SQL allows the embedding of various other languages such as SQL, C, Java or JavaScript. It goes without saying that these components should be tested with automated tests. This also applies to database triggers, which cannot be executed directly. Of course, it simplifies testing if the logic of the database triggers is stored in PL\/SQL packages and database triggers only contain simple calls to package procedures.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Is utPLSQL?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">utPLSQL is a testing suite for code in the Oracle database and is based on concepts from other unit testing frameworks such as JUnit and RSpec. The figure below shows the components of the utPLSQL suite.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql.png\"><img loading=\"lazy\" decoding=\"async\" width=\"2992\" height=\"1520\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql.png\" alt=\"utPLSQL\" class=\"wp-image-11580\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql.png 2992w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-300x152.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-1024x520.png 1024w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-768x390.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-1536x780.png 1536w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-2048x1040.png 2048w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-260x132.png 260w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-50x25.png 50w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-148x75.png 148w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-1x1.png 1w\" sizes=\"auto, (max-width:767px) 480px, (max-width:2992px) 100vw, 2992px\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Core Testing Framework<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The Core Testing Framework is installed in a dedicated schema of the Oracle database. All tables in this schema are used for temporary data or caching purposes. That means newer or older versions of utPLSQL can be installed at any time without data loss. Test suites are PL\/SQL packages which are annotated with a special comment <code>--%suite<\/code>. These special comments are called annotations and provide utPLSQL with all information for test execution. utPLSQL uses different concepts to efficiently read the required data from the Oracle data dictionary views so that tests can be executed without noticeable time delay even in schemas with more than 40 thousand PL\/SQL packages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Development<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The utPLSQL plugins for SQL Developer from Oracle and PL\/SQL Developer from Allround Automations support database developers in creating, executing and debugging tests. Code coverage reports provide information about which code is covered by tests. These two plugins are provided by the utPLSQL team. Quest and JetBrains position utPLSQL as the standard for testing code in an Oracle database and support utPLSQL directly in their products TOAD and DataGrip.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Test Automation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">utPLSQL uses reporters to produce the results of a test run in any number of output formats. The command line client or the Maven plugin can be used for this purpose. A popular output format is the JUnit since it is compatible with most tools in the CI\/CD area. TeamCity and Team Foundation Server are supported by specific reporters. For code coverage, utPLSQL provides reporters for SonarQube, Coveralls and Cobertura. Thanks to the flexible reporter concept, utPLSQL can be integrated into any CI\/CD environment. For example, in Jenkins, Bamboo, Azure DevOps, Travis CI, GitLab, GitHub Actions and many more.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Case Study And Solution Approach<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We use a schema <code>redstack<\/code> with the known Oracle tables <code>dept<\/code> and <code>emp<\/code>. In the current sprint, the following user story from our HR manager is scheduled:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">As a HR manager, I need a table with the key figures salary total, number of employees and average salary per department to assess fairness.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">I interpret the requirement literally and create a table according to listing 1.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Listing 1: table deptsal<\/span><span role=\"button\" tabindex=\"0\" data-code=\"create table deptsal (\n    deptno   number(2, 0)  not null\n       constraint deptsal_pk primary key,\n    dname    varchar2(14)  not null,\n    sum_sal  number(10, 2) not null,\n    num_emps number(4, 0)  not null,\n    avg_sal  number(7, 2)  \n       generated always as (\n          case\n             when num_emps != 0 then\n                round(sum_sal \/ num_emps, 2)\n             else\n                0\n             end\n       ) virtual\n);\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">create<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">table<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">deptsal<\/span><span style=\"color: #D4D4D4\"> (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    deptno   <\/span><span style=\"color: #569CD6\">number<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">)  <\/span><span style=\"color: #569CD6\">not null<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #569CD6\">constraint<\/span><span style=\"color: #D4D4D4\"> deptsal_pk <\/span><span style=\"color: #569CD6\">primary key<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    dname    <\/span><span style=\"color: #569CD6\">varchar2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">14<\/span><span style=\"color: #D4D4D4\">)  <\/span><span style=\"color: #569CD6\">not null<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    sum_sal  <\/span><span style=\"color: #569CD6\">number<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">not null<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    num_emps <\/span><span style=\"color: #569CD6\">number<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">4<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">)  <\/span><span style=\"color: #569CD6\">not null<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    avg_sal  <\/span><span style=\"color: #569CD6\">number<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">7<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">)  <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #569CD6\">generated<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">always<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #569CD6\">case<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #569CD6\">when<\/span><span style=\"color: #D4D4D4\"> num_emps != <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">then<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #DCDCAA\">round<\/span><span style=\"color: #D4D4D4\">(sum_sal \/ num_emps, <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #569CD6\">else<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #B5CEA8\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #569CD6\">end<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) virtual<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">I populate the table using a PL\/SQL package procedure called &#8220;etl.refresh_deptsal&#8221;. The exact implementation is not so important. If you are interested you can find it <a href=\"https:\/\/github.com\/PhilippSalvisberg\/utplsql-red-stack-demo\/blob\/a-diy-test-1-error\/src\/main\/package\/etl.pkb\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test Suite and Test Run<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Writing the code before the test is not test-driven development. Anyway, I am a pragmatist and not a dogmatist. In the end, I feel the need to check if my code does what it should do. To execute the code, I use utPLSQL. The utPLSQL extension for SQL Developer helps me to do this. For example, I can generate the skeleton of a test suite based on my existing implementation or use the code templates to create a test package, a test package body or a test procedure. The following figure shows the utPLSQL preferences of the generator and the code templates in SQL Developer.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1400\" height=\"1000\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate.png\" alt=\"utPLSQL preferences\" class=\"wp-image-11586\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate.png 1400w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-300x214.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-1024x731.png 1024w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-768x549.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-204x146.png 204w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-50x36.png 50w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-105x75.png 105w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-preferences-generate-1x1.png 1w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1400px) 100vw, 1400px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In a new worksheet, I entered <code>ut_spec<\/code> followed by <code>Ctrl-Space<\/code> to fill in the template for a test suite. Listing 2 shows the result.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Listing 2: test suite test_etl<\/span><span role=\"button\" tabindex=\"0\" data-code=\"create or replace package test_etl is\n   --%suite\n\n   --%test\n   procedure refresh_deptsal;\nend test_etl;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">create or replace<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">package<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">test_etl<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #6A9955\">--%suite<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #6A9955\">--%test<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">procedure<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">refresh_deptsal<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\"> test_etl;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The annotation <code>--%suite<\/code> identifies the package <code>test_etl<\/code> as a test suite. The annotation for the package must follow the keyword <code>is<\/code> or <code>as<\/code>. Up to the first empty line, all annotations are assigned to the package. The annotation <code>--%test<\/code> marks the procedure <code>refresh_deptsal<\/code> as a test. A test suite can consist of any number of tests. Tests can optionally be grouped with the annotations <code>--%context(&lt;name>)<\/code> and\u00a0 <code>--%endcontext<\/code>. utPLSQL supports more than 20 annotations, which are available as snippets in SQL Developer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I can now run this test suite. Either via the context menu in the editor or on a node in the navigation tree. The following figure shows the utPLSQL test runner after the execution of the test.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1650\" height=\"724\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error.png\" alt=\"utPLSQL error\" class=\"wp-image-11588\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error.png 1650w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-300x132.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-1024x449.png 1024w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-768x337.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-1536x674.png 1536w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-260x114.png 260w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-50x22.png 50w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-1-test-error-150x66.png 150w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1650px) 100vw, 1650px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The test runner uses two additional temporary connections to the database in the background. This way the SQL Developer is never blocked, which is very helpful, especially for more time-consuming tests. It is even possible to run independent tests simultaneously.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test Implementation and Test Run<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The test <code>refresh_deptsal<\/code> produces an error. The reason is obvious. The implementation of the test is missing. So, let&#8217;s create the missing package body.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Listing 3: implementation of test suite test_etl<\/span><span role=\"button\" tabindex=\"0\" data-code=\"create or replace package body test_etl is\n   procedure refresh_deptsal is\n      c_actual   sys_refcursor;\n      c_expected sys_refcursor;\n   begin\n      -- act\n      etl.refresh_deptsal;\n      \n      -- assert;\n      open c_actual for select * from deptsal;\n      open c_expected for\n         select d.deptno,\n                d.dname,\n                nvl(sum(e.sal), 0) as sum_sal,\n                nvl(count(e.empno), 0) as num_emps,\n                nvl(trunc(avg(e.sal), 2), 0) as avg_sal\n           from dept d\n           left join emp e\n             on e.deptno = d.deptno\n          group by d.deptno, d.dname;\n      ut.expect(c_actual).to_equal(c_expected)\n                         .join_by('DEPTNO');\n   end refresh_deptsal;\nend test_etl;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">create or replace<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">package body<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">test_etl<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">procedure<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">refresh_deptsal<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      c_actual   sys_refcursor;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      c_expected sys_refcursor;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">begin<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #6A9955\">-- act<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      etl.refresh_deptsal;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #6A9955\">-- assert;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">open<\/span><span style=\"color: #D4D4D4\"> c_actual <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">select<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #569CD6\">from<\/span><span style=\"color: #D4D4D4\"> deptsal;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">open<\/span><span style=\"color: #D4D4D4\"> c_expected <\/span><span style=\"color: #C586C0\">for<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #569CD6\">select<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #DCDCAA\">nvl<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">sum<\/span><span style=\"color: #D4D4D4\">(e.sal), <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> sum_sal,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #DCDCAA\">nvl<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">count<\/span><span style=\"color: #D4D4D4\">(e.empno), <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> num_emps,<\/span><\/span>\n<span class=\"line cbp-line-highlight\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #DCDCAA\">nvl<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">trunc<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">avg<\/span><span style=\"color: #D4D4D4\">(e.sal), <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">), <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> avg_sal<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">           <\/span><span style=\"color: #569CD6\">from<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">           <\/span><span style=\"color: #569CD6\">left join<\/span><span style=\"color: #D4D4D4\"> emp e<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #569CD6\">on<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #569CD6\">group by<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      ut.expect(c_actual).to_equal(c_expected)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                         .join_by(<\/span><span style=\"color: #CE9178\">&#39;DEPTNO&#39;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\"> refresh_deptsal;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\"> test_etl;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The implementation of the test in listing 3 uses the <a href=\"https:\/\/java-design-patterns.com\/patterns\/arrange-act-assert\/\">AAA pattern<\/a>. It stands for Arrange-Act-Assert. In the arrange step, the test is prepared. Here, this step is missing since the test is based on existing data. In the act step, we execute the code under test. And in the assert step, we compare the actual result with the expected result. utPLSQL provides a variety of type-safe matchers for this purpose. In this case, we compare two cursors. The data types as well as the contents of the columns are compared for all rows. The figure below shows the result after our next test run.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1648\" height=\"958\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure.png\" alt=\"utPLSQL failure\" class=\"wp-image-11590\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure.png 1648w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-300x174.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-1024x595.png 1024w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-768x446.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-1536x893.png 1536w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-251x146.png 251w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-50x29.png 50w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-129x75.png 129w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-2-test-failure-1x1.png 1w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1648px) 100vw, 1648px\" \/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now the test run produces a failure. This means that the test does not deliver the expected result. In the `Failures` tab of the test runner, the details are displayed. We see that we expected 4 rows and also got 4 rows. However, there are differences in the column <code>avg_sal<\/code> for two records. This looks like a rounding difference. Obviously, we round up in the code and round down in the test. What is correct now? I would say rounding up is common and that&#8217;s why we should adjust the test. After replacing <code>trunc<\/code> with <code>round<\/code> in the test implementation (see line 16 in listing 3), the test will complete successfully.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test Cases for Insert, Update and Delete<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The test <code>refresh_deptsal<\/code> expects data in the tables <code>dept<\/code> and <code>emp<\/code> If the tables are empty, the test still works. This is not wrong, but it shows that the quality of the test is strongly dependent on our existing data. Also, we run the risk that the test code for the assert will mirror the implementation and any errors will be repeated. Listing 4 shows an additional test based on our own test data.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:70;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Listing 4: test based on artificial data<\/span><span role=\"button\" tabindex=\"0\" data-code=\"   procedure refresh_deptsal_upd_dept_and_emp is\n      c_actual   sys_refcursor;\n      c_expected sys_refcursor;\n   begin\n      -- arrange\n      insert into dept (deptno, dname, loc)\n      values (-10, 'utPLSQL', 'Winterthur');\n      insert into emp (empno, ename, job, hiredate, sal, deptno)\n      values (-1, 'Jacek', 'Developer', trunc(sysdate), 4700, -10);\n      insert into emp (empno, ename, job, hiredate, sal, deptno)\n      values (-2, 'Sam', 'Developer', trunc(sysdate), 4300, -10);\n      \n      -- act\n      update dept set dname = 'Testing' where deptno = -10;\n      update emp set sal = 5000 where empno = -2;\n      \n      -- assert\n      open c_actual for select * from deptsal where deptno = -10;\n      open c_expected for\n         select -10 as deptno,\n                'Testing' as dname,\n                9700 as sum_sal,\n                2 as num_emps,\n                4850 as avg_sal\n           from dual;\n      ut.expect(c_actual).to_equal(c_expected).join_by('DEPTNO');\n   end refresh_deptsal_upd_dept_and_emp;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">procedure<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">refresh_deptsal_upd_dept_and_emp<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      c_actual   sys_refcursor;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      c_expected sys_refcursor;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">begin<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #6A9955\">-- arrange<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">insert<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">into<\/span><span style=\"color: #D4D4D4\"> dept (deptno, dname, loc)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">values<\/span><span style=\"color: #D4D4D4\"> (-<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;utPLSQL&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Winterthur&#39;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">insert<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">into<\/span><span style=\"color: #D4D4D4\"> emp (empno, ename, job, hiredate, sal, deptno)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">values<\/span><span style=\"color: #D4D4D4\"> (-<\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Jacek&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Developer&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #DCDCAA\">trunc<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">sysdate<\/span><span style=\"color: #D4D4D4\">), <\/span><span style=\"color: #B5CEA8\">4700<\/span><span style=\"color: #D4D4D4\">, -<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">insert<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">into<\/span><span style=\"color: #D4D4D4\"> emp (empno, ename, job, hiredate, sal, deptno)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">values<\/span><span style=\"color: #D4D4D4\"> (-<\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Sam&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Developer&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #DCDCAA\">trunc<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #DCDCAA\">sysdate<\/span><span style=\"color: #D4D4D4\">), <\/span><span style=\"color: #B5CEA8\">4300<\/span><span style=\"color: #D4D4D4\">, -<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #6A9955\">-- act<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">update<\/span><span style=\"color: #D4D4D4\"> dept <\/span><span style=\"color: #569CD6\">set<\/span><span style=\"color: #D4D4D4\"> dname = <\/span><span style=\"color: #CE9178\">&#39;Testing&#39;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">where<\/span><span style=\"color: #D4D4D4\"> deptno = -<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">update<\/span><span style=\"color: #D4D4D4\"> emp <\/span><span style=\"color: #569CD6\">set<\/span><span style=\"color: #D4D4D4\"> sal = <\/span><span style=\"color: #B5CEA8\">5000<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">where<\/span><span style=\"color: #D4D4D4\"> empno = -<\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #6A9955\">-- assert<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">open<\/span><span style=\"color: #D4D4D4\"> c_actual <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">select<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #569CD6\">from<\/span><span style=\"color: #D4D4D4\"> deptsal <\/span><span style=\"color: #569CD6\">where<\/span><span style=\"color: #D4D4D4\"> deptno = -<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">open<\/span><span style=\"color: #D4D4D4\"> c_expected <\/span><span style=\"color: #C586C0\">for<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #569CD6\">select<\/span><span style=\"color: #D4D4D4\"> -<\/span><span style=\"color: #B5CEA8\">10<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #CE9178\">&#39;Testing&#39;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #B5CEA8\">9700<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> sum_sal,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> num_emps,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #B5CEA8\">4850<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">as<\/span><span style=\"color: #D4D4D4\"> avg_sal<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">           <\/span><span style=\"color: #569CD6\">from<\/span><span style=\"color: #D4D4D4\"> dual;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      ut.expect(c_actual).to_equal(c_expected).join_by(<\/span><span style=\"color: #CE9178\">&#39;DEPTNO&#39;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\"> refresh_deptsal_upd_dept_and_emp;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">First, a department <code>-10<\/code> is created with employees <code>-1<\/code> and <code>-2<\/code>. Then the salaries of these two employees are adjusted. The expected results are based entirely on literals. Real identifiers are often positive. The use of negative values makes the tests independent of existing data. utPLSQL automatically sets a savepoint before running a test. At the end of the test, a rollback to this savepoint is performed. However, if a <code>commit<\/code> occurs somewhere, the test data with negative identifiers are quickly found and deleted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next figure shows the successful execution of further tests, which I created <a href=\"https:\/\/github.com\/PhilippSalvisberg\/utplsql-red-stack-demo\/tree\/a-diy-test-4-ins-upd-del\/src\/test\/package\">here<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1648\" height=\"1068\" src=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del.png\" alt=\"successful utPLSQL test run\" class=\"wp-image-11596\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del.png 1648w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-300x194.png 300w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-1024x664.png 1024w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-768x498.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-1536x995.png 1536w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-225x146.png 225w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-50x32.png 50w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-116x75.png 116w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql-a-diy-4-test-ins-upd-del-1x1.png 1w\" sizes=\"auto, (max-width:767px) 480px, (max-width:1648px) 100vw, 1648px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Automatic Refresh<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The table <code>deptsal<\/code> is updated by calling <code>etl.refresh_deptsal<\/code>. We can fire this call using database triggers. Listing 5 shows how.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Listing 5: After statement triggers to refresh table deptsal<\/span><span role=\"button\" tabindex=\"0\" data-code=\"create or replace trigger dept_as_iud\n   after insert or update or delete on dept\nbegin\n   etl.refresh_deptsal;\nend;\n\/\ncreate or replace trigger emp_as_iud\n   after insert or update or delete on emp\nbegin\n   etl.refresh_deptsal;\nend;\n\/\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">create or replace<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">trigger<\/span><span style=\"color: #D4D4D4\"> dept_as_iud<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">after<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">insert<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">or<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">update<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">or<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">delete<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">on<\/span><span style=\"color: #D4D4D4\"> dept<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">begin<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   etl.refresh_deptsal;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">create or replace<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">trigger<\/span><span style=\"color: #D4D4D4\"> emp_as_iud<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">after<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">insert<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">or<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">update<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">or<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">delete<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">on<\/span><span style=\"color: #D4D4D4\"> emp<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">begin<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   etl.refresh_deptsal;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">end<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now the table <code>deptsal<\/code>\u00a0 is updated after each DML statement on tables <code>emp<\/code> and <code>dept<\/code>. To test if the database triggers work, I just need to remove the call of <code>etl.refresh_deptsal<\/code> in the existing tests. For the test on the existing data, I can fire a refresh with a technical update. And voil\u00e0, all tests run through without errors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Time to commit the changes in the version control system, create a pull request, and perform a review.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Incorporating Review Comments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I showed the code to my colleague Lisa. She said that the solution is more complex than necessary and inefficient. On the one hand, the <code>deptsal<\/code> table is updated too often, for example before a <code>rollback<\/code>or when more than one DML statement is used on the underlying tables within a transaction. On the other hand, providing a table is not absolutely necessary. Even though the HR manager specifically mentions &#8220;table&#8221; in her story, it is legitimate to use a view here instead of a table. Lisa thinks that with the amount of data we have, the performance should be good enough, especially if the query is limited to a few departments. Also, these metrics are not queried that often. A view would significantly reduce our code base and simplify maintenance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Of course, Lisa is right. I can drop the table <code>deptsal<\/code>, the package <code>etl<\/code> and the database triggers. A simple view called <code>deptsal<\/code>, which is based on the fixed query in Listing 3, is sufficient here. But what do I do with my tests? &#8211; Nothing! They still describe the requirements, which have not changed, and therefore completed successfully. Unless I made a mistake when defining the view.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Refactoring is much easier with existing tests. Some people would argue that tests are what make safe refactoring possible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Core Messages And Recommendations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first steps with utPLSQL are the hardest. Make <a href=\"https:\/\/github.com\/utPLSQL\/utPLSQL\">utPLSQL<\/a> available in all your development and testing environments. For shared environments, ask your DBA for help. Install also the <a href=\"https:\/\/github.com\/utPLSQL\/utPLSQL-SQLDeveloper\">extension for SQL Developer<\/a>, it is not a requirement, but it simplifies the work with utPLSQL considerably.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start with small steps. Use utPLSQL to reproduce bugs or to test new requirements. After a short time you will experience how utPLSQL changes the way you code. You will write smaller units. You will isolate code that is difficult to test. This will make your code easier to test and easier to maintain.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nowadays, everything is about automation. Software testing is no exception. After an introduction, we will create and run utPLSQL tests with Oracle SQL Developer for a fictitious user story. Our tests will cover a PL\/SQL package, a virtual column, two database triggers and a view. The full code samples can be found<span class=\"excerpt-hellip\"> [\u2026]<\/span><\/p>\n","protected":false},"author":1,"featured_media":11610,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[13,85,87,105],"class_list":["post-11573","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle","tag-plsql","tag-sql","tag-sql-developer","tag-utplsql"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog<\/title>\n<meta name=\"description\" content=\"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\/SQL package, a virtual column, two database triggers and a view.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"og:description\" content=\"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\/SQL package, a virtual column, two database triggers and a view.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/\" \/>\n<meta property=\"og:site_name\" content=\"Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-07-30T22:39:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-11-08T15:51:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2702\" \/>\n\t<meta property=\"og:image:height\" content=\"2026\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Philipp Salvisberg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:site\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Philipp Salvisberg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/\"},\"author\":{\"name\":\"Philipp Salvisberg\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"headline\":\"Testing With utPLSQL &#8211; Made Easy With SQL Developer\",\"datePublished\":\"2022-07-30T22:39:16+00:00\",\"dateModified\":\"2023-11-08T15:51:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/\"},\"wordCount\":2167,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/utplsql_featured.png\",\"keywords\":[\"PL\\\/SQL\",\"SQL\",\"SQL Developer\",\"utPLSQL\"],\"articleSection\":[\"Oracle\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/\",\"name\":\"Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/utplsql_featured.png\",\"datePublished\":\"2022-07-30T22:39:16+00:00\",\"dateModified\":\"2023-11-08T15:51:01+00:00\",\"description\":\"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\\\/SQL package, a virtual column, two database triggers and a view.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/utplsql_featured.png\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2022\\\/07\\\/utplsql_featured.png\",\"width\":2702,\"height\":2026},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2022\\\/07\\\/31\\\/testing-with-utplsql-made-easy-with-sql-developer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Testing With utPLSQL &#8211; Made Easy With SQL Developer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\",\"name\":\"Philipp Salvisberg&#039;s Blog\",\"description\":\"Database-centric development\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\",\"name\":\"Philipp Salvisberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"width\":400,\"height\":400,\"caption\":\"Philipp Salvisberg\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\"},\"sameAs\":[\"http:\\\/\\\/www.salvis.com\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog","description":"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\/SQL package, a virtual column, two database triggers and a view.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/","og_locale":"en_US","og_type":"article","og_title":"Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog","og_description":"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\/SQL package, a virtual column, two database triggers and a view.","og_url":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/","og_site_name":"Philipp Salvisberg&#039;s Blog","article_published_time":"2022-07-30T22:39:16+00:00","article_modified_time":"2023-11-08T15:51:01+00:00","og_image":[{"width":2702,"height":2026,"url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png","type":"image\/png"}],"author":"Philipp Salvisberg","twitter_card":"summary_large_image","twitter_creator":"@phsalvisberg","twitter_site":"@phsalvisberg","twitter_misc":{"Written by":"Philipp Salvisberg","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#article","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/"},"author":{"name":"Philipp Salvisberg","@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"headline":"Testing With utPLSQL &#8211; Made Easy With SQL Developer","datePublished":"2022-07-30T22:39:16+00:00","dateModified":"2023-11-08T15:51:01+00:00","mainEntityOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/"},"wordCount":2167,"commentCount":0,"publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png","keywords":["PL\/SQL","SQL","SQL Developer","utPLSQL"],"articleSection":["Oracle"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/","url":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/","name":"Testing With utPLSQL - Made Easy With SQL Developer - Philipp Salvisberg&#039;s Blog","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#primaryimage"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png","datePublished":"2022-07-30T22:39:16+00:00","dateModified":"2023-11-08T15:51:01+00:00","description":"Creating and running utPLSQL tests with Oracle SQL Developer for a fictitious user story. Covering a PL\/SQL package, a virtual column, two database triggers and a view.","breadcrumb":{"@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#primaryimage","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2022\/07\/utplsql_featured.png","width":2702,"height":2026},{"@type":"BreadcrumbList","@id":"https:\/\/www.salvis.com\/blog\/2022\/07\/31\/testing-with-utplsql-made-easy-with-sql-developer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.salvis.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Testing With utPLSQL &#8211; Made Easy With SQL Developer"}]},{"@type":"WebSite","@id":"https:\/\/www.salvis.com\/blog\/#website","url":"https:\/\/www.salvis.com\/blog\/","name":"Philipp Salvisberg&#039;s Blog","description":"Database-centric development","publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.salvis.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515","name":"Philipp Salvisberg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","width":400,"height":400,"caption":"Philipp Salvisberg"},"logo":{"@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg"},"sameAs":["http:\/\/www.salvis.com\/"]}]}},"_links":{"self":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/11573","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/comments?post=11573"}],"version-history":[{"count":30,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/11573\/revisions"}],"predecessor-version":[{"id":12720,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/11573\/revisions\/12720"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media\/11610"}],"wp:attachment":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media?parent=11573"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/categories?post=11573"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/tags?post=11573"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}