Matlab: incorrect index matrix reference (or deception matrix)

I want to be able to write jasmine in matlab. So something like

expect(myfibonacci(0)).toBe(0); expect(myfibonacci(5)).toBe(15); expect(myfibonacci(10)).toBe(55); 

There are two strategies that I tried to implement:

(1) The first strategy uses structures

 expect = @(actual_value) struct('toBe', @(expected_value) assert(actual_value == expected_value)); 

(The actual implementation will not just call assert)

However, this does not work:

 expect(1).toBe(1); % this triggers a syntax error ??? Improper index matrix reference. % this will work: x = expect(1); x.toBe(1); 

(2) The second strategy I tried is to use a class:

 classdef expect properties (Hidden) actual_value end methods function obj = expect(actual_value) obj.actual_value = actual_value; end function obj = toBe(obj, expected_value) assert(obj.actual_value == expected_value); end end end 

At first glance it looks great: you can start the console

 expect(1).toBe(1); 

However, running this is not in the console, but in the script gives

 ??? Static method or constructor invocations cannot be indexed. Do not follow the call to the static method or constructor with any additional indexing or dot references. Error in ==> test at 1 expect(1).toBe(1); 

Is there any way to get this idea working in Matlab at all?

+6
source share
5 answers

Here's an example implementation with an overloaded subsref method. It could also be done with just one class, but it would make the subsref overload even uglier.

 classdef Tester < handle methods function obj = Tester() end function [varargout] = subsref(this,S) if S(1).type(1) =='(' tv = TestValue(S(1).subs{:}); end if numel(S) > 1 try [varargout{1:nargout}] = builtin('subsref', tv, S(2:end)); catch me me.throwAsCaller(); end else varargout{1} = tv; end end end end 

and

 classdef TestValue properties (Hidden) value; end methods function this = TestValue(value) this.value = value; end function toBe(this, v) assert( isequal(this.value, v) ); end end end 

Results in:

 >> expect(1).toBe(1) >> expect(1).toBe(2) Error using TestValue/toBe (line 13) Assertion failed. 
+2
source

In recent versions of MATLAB (13a / 13b), the built-in unit testing system is similar to what you are trying to do. Instead

 expect(myfibonacci(0)).toBe(0); 

you write

 import matlab.unittest.constraints.IsEqualTo testCase.verifyThat(myfibonacci(0), IsEqualTo(0)) 

(You can / instead of assumeThat , assertThat or fatalAssertThat ).

If for some reason you want to implement your own infrastructure, pay attention to a small difference in your syntax - you have a period, while in MathWorks there is a comma between myfibonacci(0) and the validation condition.

In MATLAB you cannot index the result of a subscript like this (well, you could, but you would have to overload subsref , and this is a world of pain, believe me). Thus, they did this in order to present the conditions of the test comparison as a separate package and use them as a separate input argument, and not as a method with point syntax.

See the documentation for the new unit test system to learn more about the structure itself or (if you want, d prefer to roll your own) syntaxes that they developed as a comparison with yours.

+7
source

Your class definition works fine if you create a function instead of a script

So, instead of testscript.m containing

 expect(myfibonacci(0)).toBe(0); expect(myfibonacci(5)).toBe(15); expect(myfibonacci(10)).toBe(55); 

you need a testfunc.m function containing

 function testfunc expect(myfibonacci(0)).toBe(0); expect(myfibonacci(5)).toBe(15); expect(myfibonacci(10)).toBe(55); 
+4
source

To add to @MohsenNosratinia's note, if you use nested functions / closures instead of OOP classes, you will get another inconsistency:

 function obj = expect(expr) obj = struct(); obj.toBe = @toBe; function toBe(expected) assert(isequal(expr,expected)) end end 
  • The syntax does not work from the command line:

     >> expect(1+1).toBe(2) Undefined variable "expect" or class "expect". 
  • Doesn't work with script:

    testScript.m

     expect(1+1).toBe(2) expect(1*1).toBe(2) 

    with the same error as before:

     >> testScript Undefined variable "expect" or class "expect". Error in testScript (line 1) expect(1+1).toBe(2) 
  • But for the function of the M file:

    testFcn.m

     function testFcn expect(1+1).toBe(2) expect(1*1).toBe(2) end 

    this is strangely accepted:

     >> testFcn Error using expect/toBe (line 5) Assertion failed. Error in testFcn (line 3) expect(1*1).toBe(2) 

    (The second statement failed as expected, but no syntax errors!)


I believe that a โ€œsyntax errorโ€ is the correct result here, since you should not directly index the result of a function call. If you do this, I think this is "undefined behavior" :) (this may work, but not in all cases!)

Instead, you must first save the result in a temporary variable, and then apply indexing to it:

 >> obj = expect(1+1); >> obj.toBe(2); 

or resort to ugly hacks , for example:

 >> feval(subsref(expect(1+1), substruct('.','toBe')), 2) 

or even undocumented features:

 >> builtin('_paren', builtin('_dot', expect(1+1), 'toBe'), 2) 
+3
source

I think it's important to recognize that MATLAB is not JavaScript. Jasmine-like syntax uses JavaScript semantics for this API. I think that when developing any API it is important and valuable to think about the language in which it is written and to subscribe not only to its technical limitations, but also to its technical merits and the idioms established by it.

As Sam said, this is the approach used in the MATLAB Unit Test Framework. For example, the constraint approach does not try to call the function immediately after any other operation or function indexing operation, but rather builds the constraint directly and calls the constraint to create a competent programming interface. One example of MATLAB's strength in this case is that it does not require a โ€œnewโ€ before building, such as Java / C # / etc. In fact, you can see that similar trade-offs are made using the Hamcrest matchers and the NUnit Limitations , none of which subscribe to the same approach to getting their competent checks, preferring instead to develop their approaches after the languages โ€‹โ€‹in which they are written.

In addition, although it is indeed a Unit Test structure, it can certainly be used to write other types of tests, such as system / integration. Given that you mentioned that you are actually writing tests, I highly recommend using an existing solution, rather than reinventing the wheel. There are quite a few investments in restrictions and other surrounding structure features, and this is definitely a manufacturing class.

+2
source

Source: https://habr.com/ru/post/953468/


All Articles