{"id":263,"date":"2015-09-17T19:19:34","date_gmt":"2015-09-17T19:19:34","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=263"},"modified":"2015-09-18T13:54:13","modified_gmt":"2015-09-18T13:54:13","slug":"tag-like-an-egyptian","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2015\/09\/17\/tag-like-an-egyptian\/","title":{"rendered":"Tag like an Egyptian"},"content":{"rendered":"\r\n\r\n<div class=\"content\"><p>Ajay Puvvala is my friend and colleague at the MathWorks. He also led the development of the TestTags feature we spoke about <a href=\"https:\/\/blogs.mathworks.com\/developer\/2015\/09\/08\/test-tags\/\">last post<\/a>.<\/p><p>Ajay, as per usual, made a great comment after last week's post which I wanted to highlight here. It had to do with the test pyramid:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015TestPyramid.png\" alt=\"\"> <\/p><p>You know the <a href=\"http:\/\/martinfowler.com\/bliki\/TestPyramid.html\">test pyramid<\/a> (<a href=\"http:\/\/googletesting.blogspot.com\/2015\/04\/just-say-no-to-more-end-to-end-tests.html\">another<\/a>)? I am sure we will have more posts discussing it in the future, but until then the fundamental principle is that you want to form a solid foundation of unit tests followed by fewer integration tests and even fewer system tests. Manual regression tests (not to be confused with exploratory and\/or user testing) should be kept to an absolute minimum, although we shouldn't ignore them when needed. The rationale here is that the farther you go up in the testing pyramid the more expensive these tests become. They are harder to write, to maintain, to run, and to make robust. They are needed, but we should opt for the pyramid as opposed to the <a href=\"\">ice cream cone<\/a>.<\/p><p>Anyway, Ajay mentioned that a test suite using tags such as the Small\/Medium\/Large tags discussed last post can help us get a sense of the health of our pyramid or ice cream cone. Yes I am a realist, I know that many of us have cones instead of pyramids. How do we get a sense of where we are?<\/p><p>To see this I will create a few tests that leverage <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/matlab_prog\/create-basic-parameterized-test.html\">test parameterization<\/a> to quickly create a large test array.<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">classdef<\/span> PyramidSuite &lt; matlab.unittest.TestCase\r\n    <span class=\"comment\">% This test class contains a nice pyramid distribution of unit tests to<\/span>\r\n    <span class=\"comment\">% system tests.<\/span>\r\n    \r\n    <span class=\"keyword\">properties<\/span>(TestParameter)\r\n        <span class=\"comment\">% To create a representative suite, create 3 parameters and implement test<\/span>\r\n        <span class=\"comment\">% methods and use different subsets of these parameters in the test<\/span>\r\n        <span class=\"comment\">% methods. Since they are combined exhaustively, the more parameters a test<\/span>\r\n        <span class=\"comment\">% uses the more test elements will get created.<\/span>\r\n        param1 = num2cell(1:20);\r\n        param2 = num2cell(1:5);\r\n        param3 = num2cell(1:3);\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Small'<\/span>})\r\n        <span class=\"keyword\">function<\/span> smallTest(testCase, param1, param2, param3)\r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Medium'<\/span>})\r\n        <span class=\"keyword\">function<\/span> mediumTest(testCase, param1, param2)\r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Large'<\/span>})\r\n        <span class=\"keyword\">function<\/span> largeTest(testCase, param1) \r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>Here I have created a nicely balanced test suite with a healthy use of small tests and lower use of larger tests in the suite. How can I be sure of this? By looking at the test tags.<\/p><pre class=\"codeinput\">import <span class=\"string\">matlab.unittest.TestSuite<\/span>;\r\npyramidSuite = TestSuite.fromClass(?PyramidSuite);\r\n\r\npyramidSizes = categorical([pyramidSuite.Tags]);\r\npie(pyramidSizes, [1 1 1]);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/TagLikeAnEgyptian_01.png\" alt=\"\"> <p>Look at that! Super simple to get a high level view of your test bed. Note this does assume that each test is tagged with one and only one of the Small\/Medium\/Large tags we are operating with here.<\/p><p>However this doesn't look like a pyramid, it looks like a pie! Alright, just for fun let's make our own test pyramid with our test tag data. We'll start by getting the tag counts, sorting them so we can build our pyramid correctly, and assign colors to reflect our preference for smaller, less expensive tests.<\/p><pre class=\"codeinput\">[counts, sizes] = histcounts(pyramidSizes);\r\n\r\n<span class=\"comment\">% Sort the values to allow creation of the pyramid<\/span>\r\n[counts, index] = sort(counts);\r\nsizes = sizes(index);\r\n\r\n<span class=\"comment\">% Assign:<\/span>\r\n<span class=\"comment\">% small tests: green<\/span>\r\n<span class=\"comment\">% medium tests: yellow<\/span>\r\n<span class=\"comment\">% large tests: red<\/span>\r\ncolors{strcmp(sizes,<span class=\"string\">'Small'<\/span>)} = <span class=\"string\">'g'<\/span>;\r\ncolors{strcmp(sizes,<span class=\"string\">'Medium'<\/span>)} = <span class=\"string\">'y'<\/span>;\r\ncolors{strcmp(sizes,<span class=\"string\">'Large'<\/span>)} = <span class=\"string\">'r'<\/span>;\r\n<\/pre><p>Great, now we can calculate a few values for the sizes of our triangles required to create a stacked pyramid where the area of each stack matches the percentage of the test suite matching the category. I'll leave the calculation here as an exercise for the reader, but the constraints at play are that the area of the largest containing triangle is 1, it's base and height are both chosen to be $\\sqrt{2}$, and the areas of each section match the percentage of corresponding categories in the suite.<\/p><pre class=\"codeinput\">total = sum(counts);\r\npercentMiddle = counts(2)\/total;\r\npercentTop = counts(1)\/total;\r\n\r\nheightBottom = sqrt(2);\r\nheightMiddle = sqrt(2*percentMiddle+percentTop);\r\nheightTop = sqrt(2*percentTop);\r\n\r\nbaseBottom = sqrt(2);\r\nbaseMiddle = (heightMiddle\/heightBottom)*baseBottom;\r\nbaseTop = (heightTop\/heightBottom)*baseBottom;\r\n<\/pre><p>Now create the endpoints of each trapezoid and the top triangle using these dimensions. Place the endpoints into an Nx2 array corresponding to <tt>(x,y)<\/tt> coordinates.<\/p><pre class=\"codeinput\"><span class=\"comment\">%Bottom section<\/span>\r\nbottom(1,:) = [0 0];\r\nbottom(2,:) = [(baseBottom-baseMiddle)\/2, heightBottom-heightMiddle];\r\nbottom(3,:) = [bottom(2,1) + baseMiddle, bottom(2,2)];\r\nbottom(4,:) = [baseBottom, 0];\r\n\r\n<span class=\"comment\">% Middle section<\/span>\r\nmiddle(1,:) = bottom(2,:);\r\nmiddle(2,:) = [middle(1,1) + (baseMiddle-baseTop)\/2, heightBottom-heightTop];\r\nmiddle(3,:) = [middle(2,1) + baseTop, middle(2,2)];\r\nmiddle(4,:) = bottom(3,:);\r\n\r\n<span class=\"comment\">% Top section<\/span>\r\ntop(1,:) = middle(2,:);\r\ntop(2,:) = [baseBottom\/2, heightBottom];\r\ntop(3,:) = middle(3,:);\r\n<\/pre><p>...and create the pyramid using <tt>fill<\/tt> and add labels at the centroid of each region.<\/p><pre class=\"codeinput\">fill(bottom(:,1), bottom(:,2), colors{3}, <span class=\"keyword\">...<\/span>\r\n    middle(:,1), middle(:,2), colors{2}, <span class=\"keyword\">...<\/span>\r\n    top(:,1), top(:,2), colors{1});\r\naxis <span class=\"string\">off<\/span>;\r\n\r\nfindCentroid = @(points) sum(points,1)\/length(points);\r\n\r\ntopCentroid = findCentroid(top);\r\ntext(topCentroid(1), topCentroid(2), sizes{1}, <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'HorizontalAlignment'<\/span>, <span class=\"string\">'center'<\/span>, <span class=\"string\">'FontSize'<\/span>, 14,<span class=\"string\">'FontWeight'<\/span>, <span class=\"string\">'bold'<\/span>);\r\n\r\nmiddleCentroid = findCentroid(middle);\r\ntext(middleCentroid(1), middleCentroid(2), sizes{2}, <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'HorizontalAlignment'<\/span>, <span class=\"string\">'center'<\/span>, <span class=\"string\">'FontSize'<\/span>, 14,<span class=\"string\">'FontWeight'<\/span>, <span class=\"string\">'bold'<\/span>);\r\n\r\nbottomCentroid = findCentroid(bottom);\r\ntext(bottomCentroid(1), bottomCentroid(2), sizes{3}, <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'HorizontalAlignment'<\/span>, <span class=\"string\">'center'<\/span>, <span class=\"string\">'FontSize'<\/span>, 14,<span class=\"string\">'FontWeight'<\/span>, <span class=\"string\">'bold'<\/span>);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/TagLikeAnEgyptian_02.png\" alt=\"\"> <p>There ya have it, a true testing pyramid built from the data found in the test tags. To convince ourselves this is working lets put it in a function and try it out on a different suite. Here is the function:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> handles = testPyramid(testSizes)\r\n[counts, sizes] = histcounts(testSizes);\r\n\r\n<span class=\"comment\">% Sort the values to allow creation of the pyramid<\/span>\r\n[counts, index] = sort(counts);\r\nsizes = sizes(index);\r\n\r\n\r\n<span class=\"comment\">% Assign:<\/span>\r\n<span class=\"comment\">% small tests: green<\/span>\r\n<span class=\"comment\">% medium tests: yellow<\/span>\r\n<span class=\"comment\">% large tests: red<\/span>\r\ncolors{strcmp(sizes,<span class=\"string\">'Small'<\/span>)} = <span class=\"string\">'g'<\/span>;\r\ncolors{strcmp(sizes,<span class=\"string\">'Medium'<\/span>)} = <span class=\"string\">'y'<\/span>;\r\ncolors{strcmp(sizes,<span class=\"string\">'Large'<\/span>)} = <span class=\"string\">'r'<\/span>;\r\n\r\n\r\ntotal = sum(counts);\r\npercentMiddle = counts(2)\/total;\r\npercentTop = counts(1)\/total;\r\n\r\nheightBottom = sqrt(2);\r\nheightMiddle = sqrt(2*percentMiddle+percentTop);\r\nheightTop = sqrt(2*percentTop);\r\n\r\nbaseBottom = sqrt(2);\r\nbaseMiddle = (heightMiddle\/heightBottom)*baseBottom;\r\nbaseTop = (heightTop\/heightBottom)*baseBottom;\r\n\r\n<span class=\"comment\">% Bottom section<\/span>\r\nbottom(1,:) = [0 0];\r\nbottom(2,:) = [(baseBottom-baseMiddle)\/2, heightBottom-heightMiddle];\r\nbottom(3,:) = [bottom(2,1) + baseMiddle, bottom(2,2)];\r\nbottom(4,:) = [baseBottom, 0];\r\n\r\n<span class=\"comment\">% Middle section<\/span>\r\nmiddle(1,:) = bottom(2,:);\r\nmiddle(2,:) = [middle(1,1) + (baseMiddle-baseTop)\/2, heightBottom-heightTop];\r\nmiddle(3,:) = [middle(2,1) + baseTop, middle(2,2)];\r\nmiddle(4,:) = bottom(3,:);\r\n\r\n<span class=\"comment\">% Top section<\/span>\r\ntop(1,:) = middle(2,:);\r\ntop(2,:) = [baseBottom\/2, heightBottom];\r\ntop(3,:) = middle(3,:);\r\n\r\nhandles = fill(bottom(:,1), bottom(:,2), colors{3}, <span class=\"keyword\">...<\/span>\r\n    middle(:,1), middle(:,2), colors{2}, <span class=\"keyword\">...<\/span>\r\n    top(:,1), top(:,2), colors{1});\r\naxis <span class=\"string\">off<\/span>;\r\n\r\nhandles(end+1) = labelSections(top, sizes{1});\r\nhandles(end+1) = labelSections(middle, sizes{2});\r\nhandles(end+1) = labelSections(bottom, sizes{3});\r\n\r\n\r\n<span class=\"keyword\">function<\/span> textHandle = labelSections(points, label)\r\ncentroid = sum(points,1)\/length(points);\r\ntextHandle = text(centroid(1), centroid(2), label, <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'HorizontalAlignment'<\/span>, <span class=\"string\">'center'<\/span>, <span class=\"string\">'FontSize'<\/span>, 14,<span class=\"string\">'FontWeight'<\/span>, <span class=\"string\">'bold'<\/span>);\r\n\r\n\r\n    \r\n\r\n<\/pre><p>Create the suite using the same parameterized testing tactic, but this time try it on a poorly implemented suite that relies too much on <tt>'Large'<\/tt> tests. I want to build the ice cream cone:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">classdef<\/span> IceCreamConeSuite &lt; matlab.unittest.TestCase\r\n    <span class=\"comment\">% This test class clearly relies on a bit too many large tests and not<\/span>\r\n    <span class=\"comment\">% enough small tests.<\/span>\r\n    \r\n    <span class=\"keyword\">properties<\/span>(TestParameter)\r\n        <span class=\"comment\">% Tweak the distribution of our parameters a bit from<\/span>\r\n        <span class=\"comment\">% the PyramidSuite to create different percentages<\/span>\r\n        param1 = num2cell(1:20);\r\n        param2 = num2cell(1:2);\r\n        param3 = num2cell(1:2);\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Small'<\/span>})\r\n        <span class=\"keyword\">function<\/span> smallTest(testCase, param1)\r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Medium'<\/span>})\r\n        <span class=\"keyword\">function<\/span> mediumTest(testCase, param1, param2)\r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n    <span class=\"keyword\">methods<\/span>(Test, TestTags={<span class=\"string\">'Large'<\/span>})\r\n        <span class=\"keyword\">function<\/span> largeTest(testCase, param1, param2, param3)\r\n        <span class=\"keyword\">end<\/span>\r\n    <span class=\"keyword\">end<\/span>\r\n    \r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><pre class=\"codeinput\">iceCreamConeSuite = TestSuite.fromClass(?IceCreamConeSuite);\r\n\r\niceCreamConeSizes = categorical([iceCreamConeSuite.Tags]);\r\n\r\nhandles = testPyramid(iceCreamConeSizes);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/TagLikeAnEgyptian_03.png\" alt=\"\"> <p>Looks correct, we now can simply rotate it to create the ice cream cone.<\/p><pre class=\"codeinput\">rotate(handles, [0 0 1], 180);\r\n<\/pre><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/TagLikeAnEgyptian_04.png\" alt=\"\"> <p>Do you strive for the test pyramid? Have you found any other methods for introspecting into your test bed to see how you are doing?<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_ad2ca810676c422d93ead2dc893fe6c9() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='ad2ca810676c422d93ead2dc893fe6c9 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' ad2ca810676c422d93ead2dc893fe6c9';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2015 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_ad2ca810676c422d93ead2dc893fe6c9()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2015b<br><\/p><\/div><!--\r\nad2ca810676c422d93ead2dc893fe6c9 ##### SOURCE BEGIN #####\r\n%%\r\n% Ajay Puvvala is my friend and colleague at the MathWorks. He also led the\r\n% development of the TestTags feature we spoke about\r\n% <https:\/\/blogs.mathworks.com\/developer\/2015\/09\/08\/test-tags\/ last post>.\r\n%\r\n% Ajay, as per usual, made a great comment after last week's post which I\r\n% wanted to highlight here. It had to do with the test pyramid:\r\n%\r\n% <<2015TestPyramid.png>>\r\n%\r\n% You know the <http:\/\/martinfowler.com\/bliki\/TestPyramid.html test\r\n% pyramid>\r\n% (<http:\/\/googletesting.blogspot.com\/2015\/04\/just-say-no-to-more-end-to-end-tests.html\r\n% another>)? I am sure we will have more posts discussing it in the future,\r\n% but until then the fundamental principle is that you want to form a solid\r\n% foundation of unit tests followed by fewer integration tests and even\r\n% fewer system tests. Manual regression tests (not to be confused with\r\n% exploratory and\/or user testing) should be kept to an absolute minimum,\r\n% although we shouldn't ignore them when needed. The rationale here is that\r\n% the farther you go up in the testing pyramid the more expensive these\r\n% tests become. They are harder to write, to maintain, to run, and to make\r\n% robust. They are needed, but we should opt for the pyramid as opposed to\r\n% the\r\n% <\r\n% ice cream cone>.\r\n%\r\n% Anyway, Ajay mentioned that a test suite using tags such as the\r\n% Small\/Medium\/Large tags discussed last post can help us get a sense of\r\n% the health of our pyramid or ice cream cone. Yes I am a realist, I know\r\n% that many of us have cones instead of pyramids. How do we get a sense of\r\n% where we are?\r\n%\r\n% To see this I will create a few tests that leverage\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/matlab_prog\/create-basic-parameterized-test.html\r\n% test parameterization> to quickly create a large test array.\r\n%\r\n% <include>PyramidSuite.m<\/include>\r\n%\r\n% Here I have created a nicely balanced test suite with a healthy use of\r\n% small tests and lower use of larger tests in the suite. How can I be\r\n% sure of this? By looking at the test tags.\r\nimport matlab.unittest.TestSuite;\r\npyramidSuite = TestSuite.fromClass(?PyramidSuite);\r\n\r\npyramidSizes = categorical([pyramidSuite.Tags]);\r\npie(pyramidSizes, [1 1 1]);\r\n\r\n%%\r\n% Look at that! Super simple to get a high level view of your test bed. Note\r\n% this does assume that each test is tagged with one and only one of the\r\n% Small\/Medium\/Large tags we are operating with here.\r\n%\r\n% However this doesn't look like a pyramid, it looks like a pie! Alright,\r\n% just for fun let's make our own test pyramid with our test tag data.\r\n% We'll start by getting the tag counts, sorting them so we can build\r\n% our pyramid correctly, and assign colors to reflect our preference for\r\n% smaller, less expensive tests.\r\n\r\n[counts, sizes] = histcounts(pyramidSizes);\r\n\r\n% Sort the values to allow creation of the pyramid\r\n[counts, index] = sort(counts);\r\nsizes = sizes(index);\r\n\r\n% Assign:\r\n% small tests: green\r\n% medium tests: yellow\r\n% large tests: red\r\ncolors{strcmp(sizes,'Small')} = 'g';\r\ncolors{strcmp(sizes,'Medium')} = 'y';\r\ncolors{strcmp(sizes,'Large')} = 'r';\r\n\r\n%%\r\n% Great, now we can calculate a few values for the sizes of our triangles\r\n% required to create a stacked pyramid where the area of each stack matches\r\n% the percentage of the test suite matching the category. I'll leave the\r\n% calculation here as an exercise for the reader, but the constraints at\r\n% play are that the area of the largest containing triangle is 1, it's base\r\n% and height are both chosen to be $\\sqrt{2}$, and the areas of each\r\n% section match the percentage of corresponding categories in the suite.\r\n% \r\n% \r\ntotal = sum(counts);\r\npercentMiddle = counts(2)\/total;\r\npercentTop = counts(1)\/total;\r\n\r\nheightBottom = sqrt(2);\r\nheightMiddle = sqrt(2*percentMiddle+percentTop);\r\nheightTop = sqrt(2*percentTop);\r\n\r\nbaseBottom = sqrt(2);\r\nbaseMiddle = (heightMiddle\/heightBottom)*baseBottom;\r\nbaseTop = (heightTop\/heightBottom)*baseBottom;\r\n\r\n%%\r\n% Now create the endpoints of each trapezoid and the top triangle using\r\n% these dimensions. Place the endpoints into an Nx2 array corresponding to\r\n% |(x,y)| coordinates.\r\n\r\n%Bottom section\r\nbottom(1,:) = [0 0];\r\nbottom(2,:) = [(baseBottom-baseMiddle)\/2, heightBottom-heightMiddle];\r\nbottom(3,:) = [bottom(2,1) + baseMiddle, bottom(2,2)];\r\nbottom(4,:) = [baseBottom, 0];\r\n\r\n% Middle section\r\nmiddle(1,:) = bottom(2,:);\r\nmiddle(2,:) = [middle(1,1) + (baseMiddle-baseTop)\/2, heightBottom-heightTop];\r\nmiddle(3,:) = [middle(2,1) + baseTop, middle(2,2)];\r\nmiddle(4,:) = bottom(3,:);\r\n\r\n% Top section\r\ntop(1,:) = middle(2,:);\r\ntop(2,:) = [baseBottom\/2, heightBottom];\r\ntop(3,:) = middle(3,:);\r\n\r\n%% \r\n% ...and create the pyramid using |fill| and add labels at the centroid of\r\n% each region.\r\nfill(bottom(:,1), bottom(:,2), colors{3}, ...\r\n    middle(:,1), middle(:,2), colors{2}, ...\r\n    top(:,1), top(:,2), colors{1});\r\naxis off;\r\n\r\nfindCentroid = @(points) sum(points,1)\/length(points);\r\n\r\ntopCentroid = findCentroid(top);\r\ntext(topCentroid(1), topCentroid(2), sizes{1}, ...\r\n    'HorizontalAlignment', 'center', 'FontSize', 14,'FontWeight', 'bold');\r\n\r\nmiddleCentroid = findCentroid(middle);\r\ntext(middleCentroid(1), middleCentroid(2), sizes{2}, ...\r\n    'HorizontalAlignment', 'center', 'FontSize', 14,'FontWeight', 'bold');\r\n\r\nbottomCentroid = findCentroid(bottom);\r\ntext(bottomCentroid(1), bottomCentroid(2), sizes{3}, ...\r\n    'HorizontalAlignment', 'center', 'FontSize', 14,'FontWeight', 'bold');\r\n\r\n%%\r\n% There ya have it, a true testing pyramid built from the data found in the\r\n% test tags. To convince ourselves this is working lets put it in a\r\n% function and try it out on a different suite. Here is the function:\r\n%\r\n% <include>testPyramid.m<\/include>\r\n%\r\n% Create the suite using the same parameterized testing tactic, but this\r\n% time try it on a poorly implemented suite that relies too much on\r\n% |'Large'| tests. I want to build the ice cream cone:\r\n%\r\n% <include>IceCreamConeSuite.m<\/include>\r\niceCreamConeSuite = TestSuite.fromClass(?IceCreamConeSuite);\r\n\r\niceCreamConeSizes = categorical([iceCreamConeSuite.Tags]);\r\n\r\nhandles = testPyramid(iceCreamConeSizes);\r\n\r\n%%\r\n% Looks correct, we now can simply rotate it to create the ice cream cone.\r\nrotate(handles, [0 0 1], 180);\r\n\r\n%%\r\n% Do you strive for the test pyramid? Have you found any other methods for\r\n% introspecting into your test bed to see how you are doing?\r\n\r\n##### SOURCE END ##### ad2ca810676c422d93ead2dc893fe6c9\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015TestPyramid.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>\r\n\r\nAjay Puvvala is my friend and colleague at the MathWorks. He also led the development of the TestTags feature we spoke about last post.Ajay, as per usual, made a great comment after last week's... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2015\/09\/17\/tag-like-an-egyptian\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":271,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/263"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/users\/90"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/comments?post=263"}],"version-history":[{"count":12,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/263\/revisions"}],"predecessor-version":[{"id":295,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/263\/revisions\/295"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/271"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=263"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=263"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=263"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}