File Exchange Pick of the Week

Our best user submissions

MockObject

Sean‘s pick this week is Mock Object by Paul Sexton.

Paul has provided a mock object that is useful for mocking inaccessible or slow software or hardware while executing unit
tests. Paul’s MockObject allows for adding methods, and output assignment (based on a call stack), and stores how it was used for later qualification.
I also like how he’s provided an example that shows the real software he’s testing and the mock object that mocks it.

In R2017a, MATLAB came out with a native mocking framework. It is used a little differently in that when you create a mock you get two objects back, a mock instance and a behavior
object for the mock. You then modify the behavior to tune how the mock object will behave.

I’ve recently been using the MATLAB mocking framework for the purpose of mocking a database connection. Here’s what I’ve
done for one of the tests, albeit mine is inside of a unit test class that inherits from matlab.mock.TestCase. I’m testing that the LastUpdateTime property of my class updates correctly when data are added to the various tables in the database.

testCase = matlab.mock.TestCase.forInteractiveUse;

import matlab.mock.actions.AssignOutputs;
import matlab.mock.constraints.WasCalled;
import matlab.mock.actions.ThrowException;

Mock the database and the cursor.

% Mock the database and the cursor
[dbmock, dbbehavior] = createMock(testCase, 'AddedMethods', ...
    {'isa', 'isopen', 'insert', 'update', 'exec'});
[cursmock, cursbehavior] = createMock(testCase, 'AddedMethods', ...
    {'close', 'fetch'}, 'AddedProperties',{'Data'});

Define the behavior of cursor and database connection. Note that these read like English sentences.

% Pass the validation tests
when(withAnyInputs(dbbehavior.isa), AssignOutputs('database.jdbc.connection'));
when(withAnyInputs(dbbehavior.isopen), AssignOutputs(true));

% Return cursor mock when database or cursor are executed/fetched.
when(withAnyInputs(dbbehavior.exec), AssignOutputs(cursmock));
when(withAnyInputs(cursbehavior.fetch), AssignOutputs(cursmock));

We need to build data that looks like what is required.

% Synthetic output times that mimic expectation of multiple hits to a
% database.
nowish = datetime('now', 'TimeZone', 'America/New_York');
t1 = table([nowish; nowish],  {'Weather'; 'Energy'}, 'VariableNames', {'lastModified', 'tableType'})
t2 = t1;
t2.lastModified(1) = t2.lastModified(1)+seconds(1); % Just Weather
t3 = t2;
t3.lastModified(2) = t3.lastModified(2)+seconds(3); % Just Energy

% Build table to insert in the format MySQL requires
c = {'01-May-2007 00:00:00',4854.40000000000,58,30;'01-May-2007 00:05:00',4802.30000000000,58.0833333333333,29.8333333333333;'01-May-2007 00:10:00',4740.30000000000,58.1666666666667,29.6666666666667;'01-May-2007 00:15:00',4700.34155844156,58.2500000000000,29.5000000000000;'01-May-2007 00:20:00',4681.77969924812,58.3333333333333,29.3333333333333;'01-May-2007 00:25:00',4648.99421487603,58.4166666666667,29.1666666666667;'01-May-2007 00:30:00',4633.70864197531,58.5000000000000,29};
data = cell2table(c, 'VariableNames', {'Time', 'N_Y_C_', 'TemperatureF_KLGA', 'Dewpoint_KLGA'});
data.Time = datetime(data.Time);
data.KLGA = [data.TemperatureF_KLGA data.Dewpoint_KLGA];
data.TemperatureF_KLGA = [];
data.Dewpoint_KLGA = [];
data = table2timetable(data)
t1 =
  2×2 table
        lastModified        tableType
    ____________________    _________
    06-Oct-2017 10:22:12    'Weather'
    06-Oct-2017 10:22:12    'Energy' 
data =
  7×2 timetable
            Time            N_Y_C_          KLGA      
    ____________________    ______    ________________
    01-May-2007 00:00:00    4854.4        58        30
    01-May-2007 00:05:00    4802.3    58.083    29.833
    01-May-2007 00:10:00    4740.3    58.167    29.667
    01-May-2007 00:15:00    4700.3     58.25      29.5
    01-May-2007 00:20:00    4681.8    58.333    29.333
    01-May-2007 00:25:00      4649    58.417    29.167
    01-May-2007 00:30:00    4633.7      58.5        29

Set up outputs for the Data property. Error after three uses.

when(get(cursbehavior.Data), ...
    then(repeat(1, AssignOutputs(t1)), ...
    then(repeat(1, AssignOutputs(t2)), ...
    then(repeat(1, AssignOutputs(t3)), ThrowException))));

Exercise the system.

% Build DatabaseHandler
db = DatabaseHandler(dbmock);

% Add energy and weather (both database tables)
addData(db, data);

Qualify database called properly and that LastUpdateTime property updates correctly. Add a 1 second buffer for processing time.

testCase.verifyThat(withAnyInputs(dbbehavior.insert()), WasCalled('WithCount', 2));
testCase.verifyThat(withAnyInputs(dbbehavior.update()), WasCalled('WithCount', 2));
firstlut = db.LastUpdateTime;
nowish = datetime('now', 'TimeZone', 'America/New_York')+seconds(1);
testCase.verifyLessThan(table2array(firstlut), nowish);
Interactive verification passed.
Interactive verification passed.
Interactive verification passed.
% Add just weather (just weather table in database)
addData(db, data(:, {'KLGA'}));
testCase.verifyThat(withAnyInputs(dbbehavior.insert()), WasCalled('WithCount', 3));
testCase.verifyThat(withAnyInputs(dbbehavior.update()), WasCalled('WithCount', 3));
secondlut = db.LastUpdateTime;
testCase.verifyEqual(secondlut.Energy, firstlut.Energy);
testCase.verifyGreaterThan(secondlut.Weather, firstlut.Weather);
Interactive verification passed.
Interactive verification passed.
Interactive verification passed.
Interactive verification passed.
% Add just energy (just energy table in database)
addData(db, data(:, {'N_Y_C_'}));
testCase.verifyThat(withAnyInputs(dbbehavior.insert()), WasCalled('WithCount', 4));
testCase.verifyThat(withAnyInputs(dbbehavior.update()), WasCalled('WithCount', 4));
thirdlut = db.LastUpdateTime;
testCase.verifyEqual(secondlut.Weather, thirdlut.Weather);
testCase.verifyGreaterThan(thirdlut.Energy, secondlut.Energy);
Interactive verification passed.
Interactive verification passed.
Interactive verification passed.
Interactive verification passed.

Going forward, I’d recommend using the MATLAB testing frameworks for your testing needs. They’re an area of MATLAB that has
really matured quite well over the last few years. If you need to design for older releases, then Paul’s MockObject can certainly help.

For another blog post on the MATLAB Mocking Framework, see this blog post on the Developer Blog.

Comments

Give both mocking frameworks a try and let us know what you think here or leave a comment for Paul.

Published with MATLAB® R2017b

|
  • print

Comments

To leave a comment, please click here to sign in to your MathWorks Account or create a new one.