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
- 类别:
- Picks
评论
要发表评论,请点击 此处 登录到您的 MathWorks 帐户或创建一个新帐户。