├── ConfigurePlot.prj ├── LICENSE ├── README.md ├── finance-conference-signature.png ├── robust2018.prj ├── runTestsWithCobeturaCodeCoverage.m ├── tbx ├── apps │ ├── ConfigurePlot.mlappinstall │ ├── ConfigurePlotAppExample.mlapp │ └── ConfigurePlotAppScreenshot.png ├── demos.xml ├── doc │ ├── GettingStarted.mlx │ └── GettingStarted.pdf ├── info.xml └── robust2018 │ ├── BankAccount.m │ ├── Contents.m │ ├── CreateMockObjectExample.mlx │ ├── DataService.m │ ├── PropertyValidation.mlx │ ├── ValidateProps.m │ ├── findArea.mlx │ ├── functionSignatures.json │ ├── html │ ├── CreateMockObjectExample.html │ ├── PropertyValidation.html │ ├── findArea.html │ └── helptoc.xml │ ├── mySafeFileTxtWriter.m │ ├── myUnsafeFileWriter.m │ └── trader.m └── test ├── TraderTest.m ├── tBankAccount.m ├── testConfigurePlotAppExample.m └── testFindArea.m /ConfigurePlot.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | ConfigurePlot 4 | Paul Peeling 5 | paul.peeling@mathworks.co.uk 6 | MathWorks 7 | 8 | 9 | Configure a MATLAB Plot 10 | 11 | ${PROJECT_ROOT}\tbx\apps\ConfigurePlotAppScreenshot.png 12 | 1.0 13 | 14 | MATLAB 15 | 16 | 17 | 1 18 | 19 | 20 | 9.4 21 | 22 | 23 | ${PROJECT_ROOT}\tbx\apps 24 | a4d5955f-6c8c-4f2f-8ff4-8c8a04d6a597 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ${PROJECT_ROOT}\tbx\apps\ConfigurePlotAppExample.mlapp 37 | 38 | 39 | 40 | 41 | 42 | C:\Users\ppeeling\git\fin-conf-2018\tbx\apps 43 | 44 | 45 | 46 | C:\Program Files\MATLAB\R2018a 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | true 58 | 59 | 60 | 61 | 62 | true 63 | 64 | 65 | 66 | 67 | true 68 | 69 | 70 | 71 | 72 | true 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | 81 | 82 | true 83 | 84 | 85 | 86 | 87 | false 88 | false 89 | true 90 | false 91 | false 92 | false 93 | false 94 | false 95 | 10.0 96 | false 97 | true 98 | win64 99 | true 100 | 101 | 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, The MathWorks, Inc. 2 | All rights reserved. 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 6 | 3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings. 7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Developing Robust MATLAB Code 2 | Paul Peeling, Mathworks 3 | 4 | As the size and complexity of your MATLAB® application increases, you want make sure to structure software projects well, ensuring users can run code without encountering unexpected behaviour or errors, for example. In this talk, you will learn about relevant advanced MATLAB software development capabilities, including error handling, object-oriented programming (OOP), unit testing, version control, and change tracking. 5 | 6 | This repository provides the code and examples used in the session. 7 | 8 | Code coverage report for this repository can be generated by the [codecov.io](https://codecov.io) service as described in a recent Developer Zone blog post: [Cov’ed Code All Throughout the Interwebs](https://blogs.mathworks.com/developer/2018/04/17/codecov-and-cobertura/) 9 | 10 | To generate code coverage, run the script 11 | ``` 12 | runTestsWithCobeturaCodeCoverage.m 13 | ``` 14 | -------------------------------------------------------------------------------- /finance-conference-signature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/finance-conference-signature.png -------------------------------------------------------------------------------- /robust2018.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | robust2018 4 | Paul Peeling 5 | ppeeling@mathworks.com 6 | MathWorks Inc. 7 | Code and Examples from "Developing Robust MATLAB Code" 8 | Code and examples accompanying the session "Developing Robust MATLAB Code" at the MATLAB Computational Finance Conference 2018 9 | ${PROJECT_ROOT}\finance-conference-signature.png 10 | 1.1 11 | ${PROJECT_ROOT}\robust2018.mltbx 12 | 13 | MATLAB 14 | 15 | 16 | 1 17 | 18 | 19 | 9.6 20 | 21 | 22 | 0b8e6fb3-8c8e-4d92-b4a0-27e66c584354 23 | 24 | true 25 | <?xml version="1.0" encoding="utf-8"?> 26 | <examples> 27 | <exampleCategory name="robust2018"> 28 | <example name="CreateMockObjectExample" type="html"> 29 | <file type="source">/robust2018/html/CreateMockObjectExample.html</file> 30 | <file type="main">/robust2018/CreateMockObjectExample.mlx</file> 31 | <file type="thumbnail"/> 32 | </example> 33 | <example name="PropertyValidation" type="html"> 34 | <file type="source">/robust2018/html/PropertyValidation.html</file> 35 | <file type="main">/robust2018/PropertyValidation.mlx</file> 36 | <file type="thumbnail"/> 37 | </example> 38 | <example name="findArea" type="html"> 39 | <file type="source">/robust2018/html/findArea.html</file> 40 | <file type="main">/robust2018/findArea.mlx</file> 41 | <file type="thumbnail"/> 42 | </example> 43 | </exampleCategory> 44 | </examples> 45 | 46 | 47 | ${PROJECT_ROOT}\tbx\apps\ConfigurePlot.mlappinstall 48 | 49 | 50 | ${PROJECT_ROOT}\tbx\info.xml 51 | ${PROJECT_ROOT}\tbx\doc\GettingStarted.mlx 52 | 53 | 54 | true 55 | 56 | 57 | 58 | R2018a 59 | latest 60 | false 61 | true 62 | true 63 | true 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | ${PROJECT_ROOT}\tbx 100 | 101 | 102 | ${PROJECT_ROOT}\tbx\apps 103 | ${PROJECT_ROOT}\tbx\demos.xml 104 | ${PROJECT_ROOT}\tbx\doc 105 | ${PROJECT_ROOT}\tbx\info.xml 106 | ${PROJECT_ROOT}\tbx\robust2018 107 | 108 | 109 | 110 | 111 | 112 | C:\Users\ppeeling\robust-matlab-2018\robust2018.mltbx 113 | 114 | 115 | 116 | C:\Program Files\MATLAB\R2019a 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | true 128 | 129 | 130 | 131 | 132 | true 133 | 134 | 135 | 136 | 137 | true 138 | 139 | 140 | 141 | 142 | true 143 | 144 | 145 | 146 | 147 | true 148 | 149 | 150 | 151 | 152 | true 153 | 154 | 155 | 156 | 157 | false 158 | false 159 | true 160 | false 161 | false 162 | false 163 | false 164 | false 165 | 10.0 166 | false 167 | true 168 | win64 169 | true 170 | 171 | 172 | -------------------------------------------------------------------------------- /runTestsWithCobeturaCodeCoverage.m: -------------------------------------------------------------------------------- 1 | %% Run tests and collect coverage information 2 | 3 | import matlab.unittest.TestRunner 4 | import matlab.unittest.TestSuite 5 | import matlab.unittest.plugins.CodeCoveragePlugin 6 | import matlab.unittest.plugins.codecoverage.CoberturaFormat 7 | 8 | % Define test suite 9 | suite = TestSuite.fromFolder( 'test' ); 10 | 11 | % Create and configure the runner 12 | runner = TestRunner.withTextOutput('Verbosity',3); 13 | src = fullfile(pwd,'tbx','robust2018'); 14 | coverageFile = fullfile(pwd, 'coverage.xml'); 15 | runner.addPlugin(CodeCoveragePlugin.forFolder(src,... 16 | 'Producing', CoberturaFormat(coverageFile))); 17 | 18 | results = runner.run(suite) 19 | 20 | %% 21 | token = '8d46bba7-22e7-4ce1-9dc5-02a052fd6b8e'; 22 | setenv( 'CODECOV_TOKEN', token ); 23 | 24 | !C:\PROGRA~1\Git\mingw64\bin\curl.exe -s https://codecov.io/bash | C:\PROGRA~1\Git\bin\bash.exe -s || - echo "Codecov did not collect coverage reports" 25 | web( 'https://codecov.io/gh/mathworks/robust-matlab-2018/' ) -------------------------------------------------------------------------------- /tbx/apps/ConfigurePlot.mlappinstall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/apps/ConfigurePlot.mlappinstall -------------------------------------------------------------------------------- /tbx/apps/ConfigurePlotAppExample.mlapp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/apps/ConfigurePlotAppExample.mlapp -------------------------------------------------------------------------------- /tbx/apps/ConfigurePlotAppScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/apps/ConfigurePlotAppScreenshot.png -------------------------------------------------------------------------------- /tbx/demos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | robust2018 4 | toolbox 5 | HelpIcon.DEMOS 6 | 7 | Code and examples accompanying the session "Developing Robust MATLAB Code" at the MATLAB Computational Finance Conference 2018 8 | 9 | 10 | 11 | 12 | other 13 | CreateMockObjectExample 14 | robust2018/html/CreateMockObjectExample.html 15 | 16 | 17 | 18 | other 19 | PropertyValidation 20 | robust2018/html/PropertyValidation.html 21 | 22 | 23 | 24 | other 25 | findArea 26 | robust2018/html/findArea.html 27 | 28 | 29 | -------------------------------------------------------------------------------- /tbx/doc/GettingStarted.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/doc/GettingStarted.mlx -------------------------------------------------------------------------------- /tbx/doc/GettingStarted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/doc/GettingStarted.pdf -------------------------------------------------------------------------------- /tbx/info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2018a 4 | robust2018 5 | toolbox 6 | 7 | robust2018\html 8 | 9 | -------------------------------------------------------------------------------- /tbx/robust2018/BankAccount.m: -------------------------------------------------------------------------------- 1 | classdef BankAccount 2 | 3 | properties 4 | AccountNumber 5 | AccountBalance 6 | AccountStatus 7 | end 8 | 9 | methods 10 | 11 | function ba = BankAccount(accountNumber, accountBalance) 12 | ba.AccountNumber = accountNumber; 13 | ba.AccountBalance = accountBalance; 14 | ba.AccountStatus = 'Open'; 15 | end 16 | 17 | function ba = deposit(ba, amount) 18 | validateattributes(amount, {'double'}, {'scalar', 'positive'}); 19 | ba.AccountBalance = ba.AccountBalance + amount; 20 | end 21 | 22 | function ba = withdraw(ba, amount) 23 | ba.AccountBalance = ba.AccountBalance - amount; 24 | if ba.AccountBalance < 0 25 | ba.AccountStatus = 'Overdraft'; 26 | end 27 | end 28 | 29 | end 30 | end -------------------------------------------------------------------------------- /tbx/robust2018/Contents.m: -------------------------------------------------------------------------------- 1 | % ROBUST2018 2 | % Version 1.0 24-May-2018 3 | % 4 | % Files 5 | % DataService - a data service to retrieve the stock price data 6 | % mySafeFileTxtWriter - mySafeFileTxtWriter writes data to a text file and cleans up 7 | % myUnsafeFileWriter - myUnsafeFileWriter writes data to a file without cleaning up 8 | % trader - an algorithm for buying stock 9 | % ValidateProps - The ValidateProps class defines three properties with validation 10 | % findArea - returns the area of shape with given dimensions 11 | % CreateMockObjectExample - test an algorithm for buying stock, but do not test the entire system 12 | % PropertyValidation - example showing validation on property values 13 | % 14 | % Copyright 2018 MathWorks Inc. 15 | -------------------------------------------------------------------------------- /tbx/robust2018/CreateMockObjectExample.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/robust2018/CreateMockObjectExample.mlx -------------------------------------------------------------------------------- /tbx/robust2018/DataService.m: -------------------------------------------------------------------------------- 1 | classdef DataService 2 | % a data service to retrieve the stock price data 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | methods (Abstract,Static) 6 | price = lookupPrice(ticker,date) 7 | end 8 | end -------------------------------------------------------------------------------- /tbx/robust2018/PropertyValidation.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/robust2018/PropertyValidation.mlx -------------------------------------------------------------------------------- /tbx/robust2018/ValidateProps.m: -------------------------------------------------------------------------------- 1 | classdef ValidateProps 2 | % The ValidateProps class defines three properties with validation 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | properties 6 | Location(1,3) double {mustBeReal, mustBeFinite} 7 | Label(1,:) char {mustBeMember(Label,{'High','Medium','Low'})} = 'Low' 8 | State(1,1) matlab.lang.OnOffSwitchState 9 | end 10 | end -------------------------------------------------------------------------------- /tbx/robust2018/findArea.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mathworks/robust-matlab-2018/1b7844d8dc9d4f3e5cbfc8a14be1dcc2af0ec160/tbx/robust2018/findArea.mlx -------------------------------------------------------------------------------- /tbx/robust2018/functionSignatures.json: -------------------------------------------------------------------------------- 1 | { 2 | "_schemaVersion": "1.0.0", 3 | "findArea": { 4 | "inputs": [ 5 | { 6 | "name": "shape", 7 | "kind": "required", 8 | "type": [ 9 | "char", 10 | "choices={'circle','rectangle'}" 11 | ], 12 | "purpose": "shape" 13 | }, 14 | { 15 | "name": "dim1", 16 | "kind": "required", 17 | "type": [ 18 | "numeric" 19 | ], 20 | "purpose": "1st dimension" 21 | }, 22 | { 23 | "name": "dim2", 24 | "kind": "ordered", 25 | "type": [ 26 | "numeric" 27 | ], 28 | "purpose": "2nd dimension" 29 | } 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /tbx/robust2018/html/CreateMockObjectExample.html: -------------------------------------------------------------------------------- 1 | 2 | Mocking Framework

Mocking Framework

  1. Create mocks for the depended-on components.
  2. Define behaviors of the mocks.
  3. Test the component of interest.
  4. Qualify interactions between the component of interest and the mocked components.

Stubbing - define behaviour

Spying - intercept and remember

Create a mock test case for interactive use.
import matlab.mock.TestCase
testCase = TestCase.forInteractiveUse
testCase =
InteractiveTestCase with no properties. 36 |

Depended on Components

The day-trading algorithm has two dependencies: a data service to retrieve the stock price data and a broker to purchase the stock.
type DataService.m
classdef DataService 37 | % a data service to retrieve the stock price data 38 | 39 | % Copyright 2018 MathWorks Inc. 40 | methods (Abstract,Static) 41 | price = lookupPrice(ticker,date) 42 | end 43 | end
In production code, there could be several concrete implementations of the DataService class, such as a BloombergDataService class.

Create Stub to Define Behavior

Create a mock for the data service dependency. The data service mock returns predefined values.
[stubDataService,dataServiceBehavior] = createMock(testCase,?DataService);
methods(stubDataService)
Methods for class matlab.mock.classes.DataServiceMock: 44 | 45 | 46 | 47 | Static methods: 48 | 49 | lookupPrice
In the DataService class, the lookupPrice method is abstract and static. The mocking framework implements this method as concrete and static.
Define behavior for the data service mock.
import matlab.unittest.constraints.IsLessThan
yesterday = datetime('yesterday');
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
"FOO",yesterday),123);
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
"FOO",IsLessThan(yesterday)),234);
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
"BAR",yesterday),765);
testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(...
"BAR",IsLessThan(yesterday)),543);
You can now call the mocked lookupPrice method.
p1 = stubDataService.lookupPrice("FOO",yesterday)
p1 = 123
p2 = stubDataService.lookupPrice("BAR",yesterday-days(5))
p2 = 543

Create Spy to Intercept Messages

Create a mock for the broker dependency.
[spyBroker,brokerBehavior] = createMock(testCase,'AddedMethods',{'buy'});
methods(spyBroker)
Methods for class matlab.mock.classes.Mock_1: 50 | 51 | buy
s2 = spyBroker.buy("inputs",[13 42])
s2 = 52 | 53 | []

Call Component Under Test

Call the trader function. Pass in the spyBroker and stubDataService mocks.
trader(stubDataService,spyBroker,"FOO",100)
trader(stubDataService,spyBroker,"FOO",75)
trader(stubDataService,spyBroker,"BAR",100)

Verify Function Interactions

Use the broker behavior object (the spy) to verify that the trader function calls the buy method, as expected.
Use the TestCase.verifyCalled method to verify that the trader function instructed the buy method to buy 100 shares of the FOO stock.
import matlab.mock.constraints.WasCalled;
testCase.verifyCalled(brokerBehavior.buy("FOO",100))
Interactive verification passed.
Verify that FOO stock was purchased two times, regardless of the specified number of shares. While the verifyCalled method is convenient to specify behavior, there is more functionality if you use the WasCalled constraint. For example, you can verify that a mocked method was called a specified number of times.
import matlab.unittest.constraints.IsAnything
testCase.verifyThat(brokerBehavior.buy("FOO",IsAnything), ...
WasCalled('WithCount',2))
Interactive verification passed.
Verify that the buy method was not called requesting 100 shares of the BAR stock.
testCase.verifyNotCalled(brokerBehavior.buy("BAR",100))
Interactive verification passed.
Although the trader function was called requesting 100 shares of BAR stock, the stub defined yesterday's price for BAR to return a higher value than all days prior to yesterday. Therefore, the broker never buys stock "BAR".
Copyright 2018 MathWorks Inc.
54 |
55 | -------------------------------------------------------------------------------- /tbx/robust2018/html/PropertyValidation.html: -------------------------------------------------------------------------------- 1 | 2 | Sample Class Using Property Validation

Sample Class Using Property Validation

The ValidateProps class defines three properties with validation.
type ValidateProps.m
classdef ValidateProps 31 | 32 | % Copyright 2018 MathWorks 33 | properties 34 | Location(1,3) double {mustBeReal, mustBeFinite} 35 | Label(1,:) char {mustBeMember(Label,{'High','Medium','Low'})} = 'Low' 36 | State(1,1) matlab.lang.OnOffSwitchState 37 | end 38 | end

Validation at Instantiation

Creating an object of the ValidateProps class performs the validation on implicit and explicit default values:
a = ValidateProps
a =
ValidateProps with properties: 39 | 40 | Location: [0 0 0] 41 | Label: 'Low' 42 | State: off 43 |
When creating the object, MATLAB:

Property Validation and Errors

a.Label = 'High'
a =
ValidateProps with properties: 44 | 45 | Location: [0 0 0] 46 | Label: 'High' 47 | State: off 48 |
The State property accepts values of 'on' and 'off' and logical true, false, 1, or 0.
a.State = true
a =
ValidateProps with properties: 49 | 50 | Location: [0 0 0] 51 | Label: 'High' 52 | State: on 53 |

Property Validation Errors

a.Location = [54 55]
Error setting property 'Location' of class 'ValidateProps':
Size of value must match specified dimensions 1×3 or be scalar.
Copyright 2018 MathWorks Inc
54 |
55 | -------------------------------------------------------------------------------- /tbx/robust2018/html/helptoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MyToolbox Toolbox 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Getting Started with the MyToolbox Toolbox 19 | System Requirements 20 | 21 | Features 22 | 23 | Feature 1 24 | 25 | Feature 2 26 | 27 | 28 | 29 | 30 | MyToolbox User Guide 32 | Setting Up MyToolbox 33 | 34 | Processing Data 35 | 36 | Verifying MyToolbox outputs 37 | Handling Test Failures 38 | 39 | 40 | 41 | 42 | 43 | Function Reference 45 | 46 | First Category 47 | 48 | function_1 49 | function_2 50 | 51 | 52 | 53 | Second Category 54 | 55 | function_3 56 | function_4 57 | 58 | 59 | 60 | Third category 61 | 62 | 63 | 64 | 65 | Mytoolbox Examples 67 | 68 | 69 | 71 | MyToolbox Web Site (Example only: goes to mathworks.com) 72 | 73 | 74 | -------------------------------------------------------------------------------- /tbx/robust2018/mySafeFileTxtWriter.m: -------------------------------------------------------------------------------- 1 | function mySafeFileTxtWriter( filename, data ) 2 | % mySafeFileTxtWriter writes data to a text file and cleans up 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | 6 | [~,~,ext] = fileparts( filename ); 7 | if ~strcmpi( ext, '.txt' ) 8 | error( 'mySafeFileTxtWriter:InvalidFileExtension', ... 9 | 'Filename input must have a ''.txt'' extension. Extension supplied was ''%s''', ... 10 | ext ); 11 | 12 | end 13 | 14 | fid = fopen( filename, 'w' ); 15 | c = onCleanup( @() fclose(fid) ); 16 | 17 | fprintf( '%s\n', data ); 18 | 19 | end 20 | -------------------------------------------------------------------------------- /tbx/robust2018/myUnsafeFileWriter.m: -------------------------------------------------------------------------------- 1 | function myUnsafeFileWriter( filename, data ) 2 | % myUnsafeFileWriter writes data to a file without cleaning up 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | 6 | fid = fopen( filename, 'w' ); 7 | fprintf( fid, '%s\n', data ); 8 | fclose( fid ); 9 | 10 | end 11 | -------------------------------------------------------------------------------- /tbx/robust2018/trader.m: -------------------------------------------------------------------------------- 1 | function trader(dataService,broker,ticker,numShares) 2 | % an algorithm for buying stock 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | 6 | yesterday = datetime('yesterday'); 7 | priceYesterday = dataService.lookupPrice(ticker,yesterday); 8 | price2DaysAgo = dataService.lookupPrice(ticker,yesterday-days(1)); 9 | 10 | if priceYesterday < price2DaysAgo 11 | broker.buy(ticker,numShares); 12 | end 13 | end -------------------------------------------------------------------------------- /test/TraderTest.m: -------------------------------------------------------------------------------- 1 | classdef TraderTest < matlab.mock.TestCase 2 | 3 | % Copyright 2018 MathWorks Inc. 4 | 5 | methods(Test) 6 | function buysStockWhenDrops(testCase) 7 | import matlab.unittest.constraints.IsLessThan 8 | import matlab.unittest.constraints.IsAnything 9 | import matlab.mock.constraints.WasCalled 10 | yesterday = datetime('yesterday'); 11 | 12 | % Create mocks 13 | [stubDataService,dataServiceBehavior] = createMock(testCase, ... 14 | ?DataService); 15 | [spyBroker,brokerBehavior] = createMock(testCase, ... 16 | 'AddedMethods',{'buy'}); 17 | 18 | % Set up behavior 19 | testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(... 20 | "FOO",yesterday),123); 21 | testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(... 22 | "FOO",IsLessThan(yesterday)),234); 23 | 24 | % Call function under test 25 | trader(stubDataService,spyBroker,"FOO",100) 26 | trader(stubDataService,spyBroker,"FOO",75) 27 | 28 | % Verify interactions 29 | testCase.verifyCalled(brokerBehavior.buy("FOO",100)) 30 | testCase.verifyThat(brokerBehavior.buy("FOO",IsAnything), ... 31 | WasCalled('WithCount',2)) 32 | end 33 | function doesNotBuyStockWhenIncreases(testCase) 34 | import matlab.unittest.constraints.IsLessThan 35 | yesterday = datetime('yesterday'); 36 | 37 | % Create mocks 38 | [stubDataService,dataServiceBehavior] = createMock(testCase, ... 39 | ?DataService); 40 | [spyBroker,brokerBehavior] = createMock(testCase, ... 41 | 'AddedMethods',{'buy'}); 42 | 43 | % Set up behavior 44 | testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(... 45 | "BAR",yesterday),765); 46 | testCase.assignOutputsWhen(dataServiceBehavior.lookupPrice(... 47 | "BAR",IsLessThan(yesterday)),543); 48 | 49 | % Call function under test 50 | trader(stubDataService,spyBroker,"BAR",100) 51 | 52 | % Verify interactions 53 | testCase.verifyNotCalled(brokerBehavior.buy("BAR",100)) 54 | end 55 | end 56 | end -------------------------------------------------------------------------------- /test/tBankAccount.m: -------------------------------------------------------------------------------- 1 | classdef tBankAccount < matlab.unittest.TestCase 2 | % Tests the BankAccount class. 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | 6 | methods (Test) 7 | 8 | function testCreation(testCase) 9 | ba = BankAccount(1234, 100); 10 | testCase.verifyEqual(ba.AccountNumber, 1234, ... 11 | 'Constructor failed to correctly set account number'); 12 | testCase.verifyEqual(ba.AccountBalance, 100, ... 13 | 'Constructor failed to correctly set account balance'); 14 | end 15 | 16 | function testDesposit(testCase) 17 | ba = BankAccount(1234, 100); 18 | ba = ba.deposit(25); 19 | testCase.verifyEqual(ba.AccountBalance, 125); 20 | end 21 | 22 | function testWithdraw(testCase) 23 | ba = BankAccount(1234, 100); 24 | ba = ba.withdraw(25); 25 | testCase.verifyEqual(ba.AccountBalance, 75); 26 | end 27 | 28 | end 29 | end -------------------------------------------------------------------------------- /test/testConfigurePlotAppExample.m: -------------------------------------------------------------------------------- 1 | classdef testConfigurePlotAppExample < matlab.uitest.TestCase 2 | 3 | % Copyright 2018 MathWorks Inc. 4 | 5 | methods (Test) 6 | function test_SampleSize(testCase) 7 | app = ConfigurePlotAppExample; 8 | testCase.addTeardown(@delete,app); 9 | 10 | testCase.type(app.SampleSizeEditField,12); 11 | testCase.press(app.UpdatePlotButton); 12 | 13 | ax = app.UIAxes; 14 | surfaceObj = ax.Children; 15 | testCase.verifySize(surfaceObj.ZData,[12 12]); 16 | end 17 | 18 | function test_Colormap(testCase) 19 | app = ConfigurePlotAppExample; 20 | testCase.addTeardown(@delete,app); 21 | 22 | testCase.choose(app.ColormapDropDown,'Winter'); 23 | testCase.press(app.UpdatePlotButton); 24 | 25 | expectedMap = winter; 26 | ax = app.UIAxes; 27 | testCase.verifyEqual(ax.Colormap,expectedMap); 28 | end 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /test/testFindArea.m: -------------------------------------------------------------------------------- 1 | classdef testFindArea < matlab.unittest.TestCase 2 | % testFindArea unit tests for findArea 3 | 4 | % Copyright 2018 MathWorks Inc. 5 | 6 | properties 7 | Circle 8 | Rectangle 9 | Figure 10 | Axes 11 | end 12 | 13 | methods ( TestClassSetup ) 14 | 15 | function drawShapesOnAxes( test ) 16 | 17 | test.Figure = figure; 18 | test.Axes = axes( 'Parent', test.Figure ); 19 | test.Rectangle = rectangle( test.Axes, 'Position', [-1 -1 1 1] ); 20 | test.Circle = rectangle( test.Axes, 'Position', [-1 -1 2 2], 'Curvature', 1 ); 21 | 22 | end 23 | 24 | end 25 | 26 | methods ( Test ) 27 | 28 | function circleIsRound( test ) 29 | 30 | expectedArea = (test.Axes.YLim(2) - test.Axes.YLim(1))^2 * pi; 31 | actualArea = findArea( 'circle', test.Circle.Position(3) ); 32 | 33 | test.verifyEqual( actualArea, expectedArea, 'AbsTol', 0.01 ); 34 | 35 | end 36 | 37 | function rectangleIsSquare( test ) 38 | 39 | expectedArea = test.Rectangle.Position(3)^2; 40 | % Second dimension is treated as 1 41 | actualArea = findArea( 'rectangle', test.Rectangle.Position(3) ); 42 | 43 | test.verifyEqual( actualArea, expectedArea, 'AbsTol', 0.01 ); 44 | 45 | end 46 | 47 | function shapeIsKnown( test ) 48 | 49 | test.verifyError( @() findArea( 'circle' ), 'MATLAB:narginchk:notEnoughInputs' ); 50 | test.verifyError( @() findArea( 'box', 3, 4 ), 'MATLAB:findArea:unrecognizedStringChoice' ); 51 | 52 | end 53 | 54 | end 55 | 56 | methods ( TestClassTeardown ) 57 | 58 | function closeFigure( test ) 59 | 60 | close( test.Figure ) 61 | 62 | end 63 | 64 | end 65 | 66 | end 67 | --------------------------------------------------------------------------------