Suppose that we have an object that has a vertical dependency from another
class Ship
def initialize
...
end
def launch
ShipLauncher.new(self, platform: platform)
end
end
class ShipLauncher
def initialize(ship, platform:)
...
end
end
And we want to test it:
it do
allow(ShipLauncher).to receive(:new)
ship = Ship.new
ship.launch
expect(ShipLauncher).to have_received(:new).with(ship, platform: 'x')
end
Until now all seems good, but if we change the ShipLauncher class like this
class ShipLauncher
def initialize(ship, platform_number:)
...
end
end
The test will pass when it shouldn't because the ShipLauncher class expects another parameter. What I'm doing wrong? I must test it with an integration test? What happen if the ShipLauncher class hides a big complexity? I have to stub all its dependencies?
There's a feature called "partial doubles" that can be used to check for this.
First, you need to enable it:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
Then write your spec like this:
describe Ship do
it "launches a ship" do
ship = Ship.new
expect(ShipLauncher).to receive(:new).with(ship, platform: 'x')
ship.launch
end
end
This will pass with the original code:
> rspec ship_spec.rb
.
Finished in 0.00429 seconds (files took 0.19664 seconds to load)
1 example, 0 failures
Now, make the change:
class ShipLauncher
def initialize(ship, platform_number:)
...
end
end
And you will get this error:
rspec ship_spec.rb
F
Failures:
1) Ship should receive new
Failure/Error: expect(ShipLauncher).to receive(:new).with(ship, platform: 'x')
Missing required keyword arguments: platform_number
# ./ship_spec.rb:30:in `block (2 levels) in <top (required)>'
Finished in 0.00437 seconds (files took 0.2022 seconds to load)
1 example, 1 failure
Note that the spec is not actually calling the initialize method on ShipLauncher, which you can verify:
class ShipLauncher
def initialize(ship, platform_number:)
raise Exception, "This code should not be run!"
end
end
Run the spec, and you'll get the same results in both cases. The partial double is simply checking that the arguments and method names match the object being mocked out.
Related
I am looking into Nebulex.Caching and use the followint decorator:
#decorate cacheable(cache: Cache, key: name)
def get_by_name(name, age) do
# your logic (maybe the loader to retrieve the value from the SoR)
end
It works perfectly. However, when I turn off Redis, it fails with Elixir.Redix.ConnectionError. This issue is well described here: https://github.com/cabol/nebulex_ecto/issues/6
I tried to write my own decorator and reuse the code from Nebulex.Caching:
defmodule Coups.Inventory.Cache.Decorator do
use Decorator.Define, [safe_cacheable: 1]
require Logger
def safe_cacheable(attrs, block, context) do
try do
Nebulex.Caching.cacheable(attrs, block, context)
rescue Elixir.Redix.ConnectionError ->
quote do
unquote(block)
end
end
end
end
However, it didn't change anything, and the exception was not caught. How can I achieve the expected behavior?
I'm doing an exercise on a course and I have to write code to make the following test pass:
it 'returns itself when exiting a journey' do
expect(subject.finish(station)).to eq(subject)
end
I've written:
class Journey
...
def finish(station)
Journey
end
...
end
I get the error:
expected: #<Journey:0x00007fd90091e7c8>
got: Journey
(compared using ==)
Diff:
## -1,2 +1,2 ##
-#<Journey:0x00007fd90091e7c8>
+Journey
It felt a bit too easy when I was writing it but I'm not sure how else to go about this. Any help would be greatly appreciated, thanks!
You're returning a class
def finish(station)
Journey
end
but you need to return an instance:
def finish(station)
Journey.new
end
But that's not good enough yet, because you need to return the same instance as the object on which the method was called on"
def finish(station)
self
end
I would like to access the class name of the concrete class that's invoking a static method implemented in an abstract superclass.
This is the code (part of) of the abstract superclasss:
classdef (Abstract) AbstractJobProcessor < handle
properties (Abstract, Constant)
VERSION_MAJOR;
VERSION_MINOR;
LAST_MODIFIED;
end
...
methods (Static)
function res = getVersionMajor;
res = AbstractJobProcessor.VERSION_MAJOR;
end
function res = getVersionMinor
res = AbstractJobProcessor.VERSION_MINOR;
end
function res = getVersionInfo
res = sprintf('**CLASSNAME**: v%d.%02d (last modified: %s)',...
AbstractJobProcessor.VERSION_MAJOR,...
AbstractJobProcessor.VERSION_MINOR,...
AbstractJobProcessor.LAST_MODIFIED);
end
end
...
Basically, I would like to access the classname of the concrete subclass and use it in the method getVersionInfo in place of the string **CLASSNAME**.
All the methods returning meta information about a class (that I have found in the documentation) require a reference to an instance of the class (like, for example, mc = metaclass(object)).
The below function will give you what you want - subclass name, that was used when invoking an (inherited) static superclass method. Just call it inside your superclass method like you would any normal function:
className = getStaticCallingClassName();
What it does handle:
Both the case when method was invoked programmatically (i.e. by a running script / function), as well as when it was invoked from the command window.
Arbitrarily nested package names (i.e. classes located inside directories prefixed with +).
What it does not handle:
Does not work if the static method is called in a non-static context, i.e. on an object instance. But you should not be using such syntax anyway. This would've been possible if we were able to use evalin with 'caller' workspace recursively, but it does not work this way.
A brief explanation behind the idea: second entry in the stack trace, produced by dbstack, would correspond to the superclass, which we can use to extract the static method name. The next steps depend on:
If the method is invoked programmatically, third stack entry would point us to a line in the the parent script/function which we need to read, e.g. using dbtype. All that's left to do is extract the subclass name using regexp based on the method name.
If the method is invoked from command window, we query the last command and use that as the input for our regular expression.
Note that even if stack has 3 entries or more, it doesn't mean that the method was invoked programmatically. For example, if we've stopped on a breakpoint somewhere and invoke the method from command window, stack trace would be long, but regexp based on the line from the third stack trace entry will not give us the answer. In this case we fall back to the command window approach.
Warning: it heavily relies on undocumented features and may break in any feature release. Tested on Matlab 2015b, but should work on most previous releases as well. Some may say it is quite dirty, but it works very well, and it's the only method that I'm aware of to achieve such a behavior.
function [className, fullPath] = getStaticCallingClassName()
ST = dbstack('-completenames');
% First one is getStaticCallingClassName, second one is the superclass
methodName = char(regexp(ST(2).name, '[^\.]([^.]*)$', 'match'));
% matches string (combination of alphanumeric/underscore/dot characters) preceeding the given method call.
pattern = sprintf('[\\w.-]*(?=.%s)', methodName);
% If the parent called static method programmatically, we should be able to find it via the next (third) stack trace
if length(ST) > 2
command = evalc('dbtype(ST(3).file, num2str(ST(3).line))');
className = char(regexp(command, pattern, 'match'));
else % was likely called from command window. Long stack trace means that we're simply waiting in a breakpoint somewhere
className = []; % go straight to command window approach
end
if isempty(className) % means that static method was called directly from command window
javaHistory = com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory();
command = char(javaHistory(end));
className = char(regexp(command, pattern, 'match'));
end
fullPath = which(className);
end
Here's a workaround. According to the MATLAB documentation:
'Ordinary methods define functions that operate on objects of the class',
'Static methods are (1) associated with a class, but (2) not with specific instances of that class'.
You can have both aspects of static methods if you call an ordinary method with an empty object array.
For example, suppose we have a base class:
classdef base
methods
function obj = base()
disp('constructor called')
end
function dispClassName(obj)
disp(['class name = ', class(obj)]);
end
end
end
and a subclass
classdef sub < base
end
Now call the methods as follows (this will not invoke any constructor):
>> base.empty.dispClassName
class name = base
>> sub.empty.dispClassName
class name = sub
A real solution (for which I did an enhancement request 03315500 to MathWorks) would be to extend the MATLAB language with a method attribute 'Class' to define methods that are associated with the invoking class (similar to the Python #classmethod decorator). Methods of this class would automatically receive the metaclass of the invoking function as a first argument. With such an extension we could define a base class:
% Future MATLAB syntax extension
classdef base
methods(Class) % New method attribute ‘Class’
function dispClassName(cls) % implicit argument (meta.class)
disp(['class name = ' cls.Name ]);
end
end
end
and a subclass
classdef sub < base
end
and call
>> base.dispClassName
class name = base
>> sub.dispClassName
class name = sub
I have set a path in my pathdef (C:\MyFolder\Classes) where I have the BankAccount and AccountManager class examples from MATLAB, I have just added Test to the end of each name for testing purposes.
So when in a different path I can run the line below and it works fine. I step through and see it open the BankAccountTest file.
mb = BankAccountTest(12345, 1000);
Now I have tried adding a new class say called MyFuncs to the same directory as the classes above.
classdef MyFuncs
methods(Static)
function [mynum] = test_func(intOne, intTwo)
mynum = intOne * intTwo;
end
end
end
When I try the line below
nm = MyFuncs.test_func(5, 6);
I get the error message,
Undefined variable "MyFuncs" or class "MyFuncs.test_func".
Why can it not 'see' this class but it can see the others?
Answer
Closing my Matlab and and re-opening it seems to have done the trick
From my understanding, the return value from a factory's 'to_create' method is ignored. This means that the object returned from the 'build' or 'initialize_with' portion of the factory is the object ultimately returned when calling 'create' within a test.
In my case, I am using a variant of the Repository Pattern. I am overriding the 'to_create' portion of the factory to include a call to a repository 'save' method. This method does not modify the given object, but returns an object representing the persisted form of the original.
However, the instance returned from the 'build' block is returned from the factory, and not the instance created in the 'to_create' block. In my code, this means the "unpersisted" form of the object is returned, not the object with updated attributes (e.g. 'id') from the saving action.
Is there a way of forcing the return value of 'create' to be either the result of the 'to_create' block or some value generated within that block?
class Foo
attr_accessor :id, :name
...
end
class FooRepository
def self.create(name)
Foo.new(name) # this object is not yet persisted and has no .id
end
def self.save(foo)
# this method must not guarantee that the original Foo instance
# will always be returned
...
updated_foo # this is a duplicate of the original object
end
...
end
FactoryGirl.define do
factory :foo, class: FooRepository do
# create an example Foo
initialize_with { FooRepository.create(name: "Example") }
# save the Foo to the datastore, returning what may be a duplicate
to_create {|instance| FooRepository.save(instance)}
end
end
describe FooRepository do
it "saves the given Foo to the datastore" do
foo = create(:foo)
foo.id #=> nil
...
end
end
I don't have an answer for you beyond "raise an issue", sorry.
The default to_create callback looks like this:
$ grep to_create lib/factory_girl/configuration.rb
to_create {|instance| instance.save! }
The main problem is that ActiveRecord modifies itself in place when you call save! on it. FactoryGirl will ignore any new objects that are returned from to_create.
A quick hack if you want to override the default create strategy:
module FactoryGirl
module Strategy
class Create
def association(runner)
runner.run
end
def result(evaluation)
evaluation.object.tap do |instance|
evaluation.notify(:after_build, instance)
evaluation.notify(:before_create, instance)
instance = evaluation.create(instance) # <-- HACK
evaluation.notify(:after_create, instance)
end
end
end
end
end
... Or do this to your to_create hook to mimic Rails' in-place modification:
to_create do |record|
new_record = YourRepositoryHere.new.create(record)
record.attributes = new_record.attributes # For example
new_record # Return the record just in case the bug is fixed
end
Best of luck. :(