{"id":2456,"date":"2020-08-19T20:41:34","date_gmt":"2020-08-20T00:41:34","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=2456"},"modified":"2020-08-20T16:46:46","modified_gmt":"2020-08-20T20:46:46","slug":"tests-in-projects","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2020\/08\/19\/tests-in-projects\/","title":{"rendered":"Testing out projects a bit more"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p>So now you <a href=\"https:\/\/blogs.mathworks.com\/developer\/2020\/06\/11\/project-yourself\/\">know a bit about projects<\/a> . You can see how they enable you to create a standard environmental setup so that you (and others!) can reproduce the right conditions to get going without fuss or learning curves. It gives you a fundamental unit of development so that all contributors know how to immediately get going and contribute to your work.<\/p><p>For kicks, I wanted to show how projects can begin to improve the workflow for a real example project that is out there, show how projects can be used for existing code, and maybe while we're at it we can get into a few more of the benefits projects have to offer.<\/p><p>So with that, let me introduce <a href=\"https:\/\/github.com\/libDirectional\/libDirectional\">libDirectional<\/a>! What does it do? I actually have very little clue. It is a library related to \"directional statistics as well as recursive estimation on directional manifolds\", a topic which to be honest I know very little about. However, it looks like a project that is quite well put together. It has good documentation, a nice consistent way to build its mex files and other artifacts derived from the source code, it provides and manages its dependencies, and it has a great comprehensive test suite. Kudos libDirectional!<\/p><!--\/introduction--><p>I think that projects can help take it to the next level! This project is already in git, so let's go ahead and just turn it into a project by creating a new project from git:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPNewProject.gif\" alt=\"\"> <\/p><p>You will then be prompted for the repository:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPNewFromGit.png\" alt=\"\"> <\/p><p>and to confirm the name of the project:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPProjName.png\" alt=\"\"> <\/p><p>As Laura showed you <a href=\"https:\/\/blogs.mathworks.com\/developer\/2020\/06\/11\/project-yourself\/\">last time<\/a>, you can leverage projects to ensure that the right paths are added inherently to the project on startup. Installation instructions are no longer needed, a new contributor can simply load the project. This also means that the startup file, which essentially handles path management, isn't needed anymore.<\/p><pre class=\"language-matlab\">\r\n[pathstr,~,~] = fileparts(mfilename(<span class=\"string\">'fullpath'<\/span>));\r\naddpath(genpath([pathstr <span class=\"string\">'\/lib'<\/span>]))\r\nclearvars <span class=\"string\">pathstr<\/span>\r\n\r\n<\/pre><p>The project setup guides you through this, and we can now go ahead and remove the startup file which isn't needed anymore:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPPath.gif\" alt=\"\"> <\/p><p>Note that once the project is setup you can easily turn this into a toolbox to distribute to users of the project who just want to <i>use<\/i> it, and don't really need to <i>contribute<\/i> to it, but I'll leave that for you to explore as an exercise.<\/p><p>So now a contributor can get up and running simply by cloning the git repository and launching the project. Done. No instructions needed. A user can get up and running simply by installing the toolbox as an add on. Lovely.<\/p><p>But wait there's more! There is also a relatively simple, yet powerful capability built into projects that is worth highlighting, and which starts to kick us off into additional workflows, such as ensuring quality through continuous integration.<\/p><p>The key is this -- projects know how to test themselves. Or perhaps they enable the project author to encode precisely how to test the project so that other people (such as colleagues) and computers (such as CI build agents) can simply ask, \"Project, test yourself!\" and it is off to the races. This is done simply by adding \"Test\" labels as metadata to the test files, and then, well, that's it!<\/p><p>In fact, you may not even need to add a <tt>Test<\/tt> label, since the project makes a solid effort at identifying what is a test and adding the label for you. However, it may not do this for all tests, such as script-based tests or tests that inherit from subclasses of <tt>matlab.unittest.TestCase<\/tt>, so be sure to confirm all your tests are labeled appropriately. In this case, projects do a nice job of labeling them all appropriately by default. If you want to exclude some tests from your \"standard\" project test suite you can do this by removing the label as shown below. This might come in handy in some situations, such as when some of your tests take a long time to execute and you only want to run them in special deferred test runs.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPTestLabels.gif\" alt=\"\"> <\/p><p>Now with your tests labeled appropriately with project metadata, you can actually create a test suite directly from the project and run it. It's as easy as navigating to the project root and calling <tt>runtests<\/tt>. Equivalently, you can simply pass the root folder of the project to the <tt>runtests<\/tt> function:<\/p><pre class=\"codeinput\">runtests <span class=\"string\">libDirectional<\/span>\r\n<\/pre><pre class=\"codeoutput\">Setting up ProjectFixture\r\nDone setting up ProjectFixture: Opened project 'libDirectional'.\r\n__________\r\n\r\nRunning TessellateS3Test\r\n.\r\nDone TessellateS3Test\r\n__________\r\n\r\nRunning AbstractCircularDistributionTest\r\n.....\r\nDone AbstractCircularDistributionTest\r\n__________\r\n\r\n\r\n... (SNIP) ...\r\n\r\n\r\nTearing down ProjectFixture\r\nDone tearing down ProjectFixture: Closed project 'libDirectional'.\r\n__________\r\n\r\n\r\n<\/pre><p>Note in the output above, that this leverages a <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.fixtures.projectfixture-class.html\">ProjectFixture<\/a>. This is key, because the ProjectFixture loads the project, thus ensuring the development environment is set up properly with the correct path and other requirements before running the tests.<\/p><p>You can also customize your test runner with various plugins. In this case you can use the <tt>testsuite<\/tt> function to (similarly) create the tests for the project (complete with the ProjectFixture attached), and then create and tune the runner you'd like to use for these tests to your hearts content so you can do things like produce TAP or JUnit style test artifacts, PDF or html reports, coverage, Simulink Test customizations and many many other things you may want to do.<\/p><p>All you need to do now to set up your project for testing in CI is simply a git clone, followed by a <tt>matlab -batch assertSuccess(runtests)<\/tt> from the project root (which likely is the root of the git repo you just cloned). Done. Easy peasy. Let's keep you worrying about things other than your CI and development setup. You know, really hard and important things, like \"directional statistics as well as recursive estimation on directional manifolds\".<\/p><p>Ready? Go!<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_ab794bb0c4d14a619a6c43cf0a5dff05() {\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='ab794bb0c4d14a619a6c43cf0a5dff05 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' ab794bb0c4d14a619a6c43cf0a5dff05';\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 2020 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_ab794bb0c4d14a619a6c43cf0a5dff05()\"><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; R2020a<br><\/p><\/div><!--\r\nab794bb0c4d14a619a6c43cf0a5dff05 ##### SOURCE BEGIN #####\r\n%%\r\n% So now you\r\n% <https:\/\/blogs.mathworks.com\/developer\/2020\/06\/11\/project-yourself\/ know\r\n% a bit about projects> . You can see how they enable you to create a\r\n% standard environmental setup so that you (and others!) can reproduce the\r\n% right conditions to get going without fuss or learning curves. It gives\r\n% you a fundamental unit of development so that all contributors know how\r\n% to immediately get going and contribute to your work.\r\n%\r\n% For kicks, I wanted to show how projects can begin to improve the\r\n% workflow for a real example project that is out there, show how projects\r\n% can be used for existing code, and maybe while we're at it we can get\r\n% into a few more of the benefits projects have to offer.\r\n%\r\n% So with that, let me introduce\r\n% <https:\/\/github.com\/libDirectional\/libDirectional libDirectional>! What\r\n% does it do? I actually have very little clue. It is a library related to\r\n% \"directional statistics as well as recursive estimation on directional\r\n% manifolds\", a topic which to be honest I know very little about. However,\r\n% it looks like a project that is quite well put together. It has good\r\n% documentation, a nice consistent way to build its mex files and other\r\n% artifacts derived from the source code, it provides and manages its\r\n% dependencies, and it has a great comprehensive test suite. Kudos\r\n% libDirectional!\r\n%\r\n%% \r\n% I think that projects can help take it to the next level! This project is\r\n% already in git, so let's go ahead and just turn it into a project by\r\n% creating a new project from git:\r\n%\r\n% <<y2020TIPNewProject.gif>>\r\n%\r\n% You will then be prompted for the repository:\r\n%\r\n% <<y2020TIPNewFromGit.png>>\r\n%\r\n% and to confirm the name of the project:\r\n%\r\n% <<y2020TIPProjName.png>>\r\n%\r\n% As Laura showed you\r\n% <https:\/\/blogs.mathworks.com\/developer\/2020\/06\/11\/project-yourself\/ last\r\n% time>, you can leverage projects to ensure that the right paths are added\r\n% inherently to the project on startup. Installation instructions are no\r\n% longer needed, a new contributor can simply load the project. This also\r\n% means that the startup file, which essentially handles path management,\r\n% isn't needed anymore. \r\n%\r\n% <include>libDirectional\/startup.m<\/include>\r\n%\r\n% The project setup guides you through this, and we can now go ahead and\r\n% remove the startup file which isn't needed anymore:\r\n% \r\n% <<y2020TIPPath.gif>>\r\n% \r\n% Note that once the project is setup you can easily turn this into a\r\n% toolbox to distribute to users of the project who just want to _use_ it,\r\n% and don't really need to _contribute_ to it, but I'll leave that for you\r\n% to explore as an exercise.\r\n%\r\n%\r\n% So now a contributor can get up and running simply by cloning the git\r\n% repository and launching the project. Done. No instructions needed. A\r\n% user can get up and running simply by installing the toolbox as an add\r\n% on. Lovely.\r\n%\r\n% But wait there's more! There is also a relatively simple, yet powerful\r\n% capability built into projects that is worth highlighting, and which\r\n% starts to kick us off into additional workflows, such as ensuring quality\r\n% through continuous integration.\r\n%\r\n% The key is this REPLACE_WITH_DASH_DASH projects know how to test themselves. Or perhaps they\r\n% enable the project author to encode precisely how to test the project so\r\n% that other people (such as colleagues) and computers (such as CI build\r\n% agents) can simply ask, \"Project, test yourself!\" and it is off to the\r\n% races. This is done simply by adding \"Test\" labels as metadata to the\r\n% test files, and then, well, that's it!\r\n% \r\n% In fact, you may not even need to add a |Test| label, since the project\r\n% makes a solid effort at identifying what is a test and adding the label\r\n% for you. However, it may not do this for all tests, such as script-based\r\n% tests or tests that inherit from subclasses of\r\n% |matlab.unittest.TestCase|, so be sure to confirm all your tests are\r\n% labeled appropriately. In this case, projects do a nice job of labeling\r\n% them all appropriately by default. If you want to exclude some tests from\r\n% your \"standard\" project test suite you can do this by removing the label\r\n% as shown below. This might come in handy in some situations, such as when\r\n% some of your tests take a long time to execute and you only want to run\r\n% them in special deferred test runs.\r\n%\r\n% <<y2020TIPTestLabels.gif>>\r\n%\r\n% Now with your tests labeled appropriately with project metadata, you can\r\n% actually create a test suite directly from the project and run it. It's\r\n% as easy as navigating to the project root and calling |runtests|.\r\n% Equivalently, you can simply pass the root folder of the project to the\r\n% |runtests| function:\r\nruntests libDirectional\r\n\r\n%%\r\n% Note in the output above, that this leverages a\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.fixtures.projectfixture-class.html\r\n% ProjectFixture>. This is key, because the ProjectFixture loads the\r\n% project, thus ensuring the development environment is set up properly\r\n% with the correct path and other requirements before running the tests.\r\n%\r\n% You can also customize your test runner with various plugins. In this\r\n% case you can use the |testsuite| function to (similarly) create the tests\r\n% for the project (complete with the ProjectFixture attached), and then\r\n% create and tune the runner you'd like to use for these tests to your\r\n% hearts content so you can do things like produce TAP or JUnit style test\r\n% artifacts, PDF or html reports, coverage, Simulink Test customizations\r\n% and many many other things you may want to do.\r\n%\r\n% All you need to do now to set up your project for testing in CI is simply\r\n% a git clone, followed by a |matlab -batch assertSuccess(runtests)| from\r\n% the project root (which likely is the root of the git repo you just\r\n% cloned). Done. Easy peasy. Let's keep you worrying about things other\r\n% than your CI and development setup. You know, really hard and important\r\n% things, like \"directional statistics as well as recursive estimation on\r\n% directional manifolds\".\r\n%\r\n% Ready? Go!\r\n##### SOURCE END ##### ab794bb0c4d14a619a6c43cf0a5dff05\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2020TIPTestLabels-Featured.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><!--introduction--><p>So now you <a href=\"https:\/\/blogs.mathworks.com\/developer\/2020\/06\/11\/project-yourself\/\">know a bit about projects<\/a> . You can see how they enable you to create a standard environmental setup so that you (and others!) can reproduce the right conditions to get going without fuss or learning curves. It gives you a fundamental unit of development so that all contributors know how to immediately get going and contribute to your work.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2020\/08\/19\/tests-in-projects\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":2470,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[31,7],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2456"}],"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=2456"}],"version-history":[{"count":7,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2456\/revisions"}],"predecessor-version":[{"id":2482,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/2456\/revisions\/2482"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/2470"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=2456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=2456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=2456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}