Giter Club home page Giter Club logo

sinon's People

Contributors

gitter-badger avatar jonathan-benn-copilot avatar note35 avatar tirkarthi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sinon's Issues

base.py exposes protected attributes

In base.py, there are several entities (attributes, methods, classes), including the following:

Pure
wrap2spy
unwrap
wrap2stub
args_type
pure
obj
prop
orig_func

These entities should probably be renamed to add a _ prefix (protected), to discourage end-users from trying to use them. The entities should be protected because they are used in child classes like SinonSpy and SinonStub.

Missing Stub Behaviour API

The following Stub Behaviour API functions are missing:

sinon.createStubInstance
# If you want to create a stub object of MyConstructor, but don’t want the constructor
# to be invoked, use this utility function.

stub.resetBehavior();
# Resets the stub’s behaviour to the default behaviour

stub.resetHistory();
# Resets the stub’s history

stub.returnsArg(index);
# Causes the stub to return the argument at the provided index.
# stub.returnsArg(0); causes the stub to return the first argument.

stub.throws(function() { return new Error(); });
# Causes the stub to throw the exception returned by the function.

stub.callThrough();
# Causes the original method wrapped into the stub to be called when none 
# of the conditional stubs are matched.

Class-based method stub cannot chain throws

class A:
	def B(self):
		return 5
stub = sinon.stub(A, 'B').onFirstCall().throws(BaseException('Hello World')).onSecondCall().returns(42)
stub() # error
stub()
stub()
stub.restore()

Should not be able to restore an anonymous (pure) stub

For anonymous (pure) stubs, that is to say a stub created via stub = sinon.stub() that is not overriding another function, the Sinon.JS library does not allow you to call the restore() method.

e.g.

stub = sinon.stub()
stub.restore() // throws "TypeError: stub.restore is not a function"

While the exact error message doesn't matter too much, Sinon.PY should probably also throw an error. It doesn't make sense to try to restore a pure stub.

Cannot stub logging with open stream

I'm not 100% sure this is a bug, or just something that we can't do anything about. But it's possible that a different internal implementation would allow the following code to execute.

This code succeeds:

import sinon
import logging
log = logging.getLogger('test')
stub = sinon.stub(log, 'warning')

This code fails:

import sinon
import logging
import sys
log = logging.getLogger('test')
streamHandler = logging.StreamHandler(sys.stdout)
log.addHandler(streamHandler)
stub = sinon.stub(log, 'warning') # raises "TypeError: can't pickle thread.lock objects"

args_list not recording single arguments correctly

There is an issue with args_list where it does not correctly record the call spy(). It only works correctly for one or more arguments, such as spy(1, 2)

spy = sinon.spy()
spy()
assert spy.args == [()]  # fails, equals []

Missing Stub Weird API

Here are some weird Stub API functions that are missing:

sinon.addBehavior(name, fn);
# Add a custom behavior. The name will be available as a function on stubs,
# and the chaining mechanism will be set up for you

stub.get(getterFn)
# Replaces a new getter for this stub.

stub.set(setterFn)
# Defines a new setter for this stub.

stub.value(newVal)
# Defines a new value for this stub.

Are these needed?

Stub.onCall has off-by-one error

class C():
    def f2(self, x):
        return x*2
c = C()
stub = sinon.stub(c, 'f2')
stub.onCall(0).returns(0)
stub.onCall(1).returns(1)
stub.onCall(2).returns(2)
assert c.f2() == 0 # fails, returns 1
assert c.f2() == 1
assert c.f2() == 2

Module-based stub cannot chain throws

import sinon
import urllib2
stub = sinon.stub(urllib2, 'urlopen').onFirstCall().throws(BaseException('Hello World')).onSecondCall().returns(42)
stub() # error
stub()
stub()
stub.restore() # error

onCall(...).withArgs(...) not allowed

If you call this code:
stub.onCall(0).withArgs(42).returns("What's the meaning of life?")

It should raise an exception like the following:
Error: Defining a stub by invoking "stub.onCall(...).withArgs(...)" is not supported. Use "stub.withArgs(...).onCall(...)" to define sequential behavior for calls with certain arguments.

Deprecation warning due to invalid escape sequences

Deprecation warnings are raised due to invalid escape sequences. This can be fixed by using raw strings or escaping the literals. pyupgrade also helps in automatic conversion : https://github.com/asottile/pyupgrade/

find . -iname '*.py' | grep -v example | xargs -P4 -I{} python3.8 -Wall -m py_compile {}
./sinon/test/TestSinonMatcher.py:53: DeprecationWarning: invalid escape sequence \w
  m = SinonMatcher("(\w*) (\w*)", strcmp="regex")
./sinon/test/TestSinonMatcher.py:65: DeprecationWarning: invalid escape sequence \w
  m = SinonMatcher("(\w*) (\w*)", strcmp="regex")

Immediately creating Stub replacement is deprecated

var stub = sinon.stub(object, "method", func);
# This has been removed from v3.0.0. Instead you should use
# stub(obj, 'meth').callsFake(fn)

Do you want to remove this 3-argument version of sinon.stub?

Combined withArgs and onCall not working correctly

stub = sinon.stub()
stub.withArgs('A').onFirstCall().returns(1)
stub.withArgs('A').onSecondCall().returns(2)
stub.withArgs('B').onFirstCall().returns(10)
stub.withArgs('B').onSecondCall().returns(20)
assert stub() == None
assert stub('A') == 1  # fails, returns 2
assert stub('A') == 2  # fails, returns None
assert stub('A') == None
assert stub('B') == 10  # fails, returns None
assert stub('B') == 20  # fails, returns None
assert stub('B') == None

Calling stub.reset() makes stub no longer override return value

import sinon
import os
os.system("cd") # returns 0
stub = sinon.stub(os, "system").returns(5)
os.system("cd") # returns 5
stub.callCount # returns 1
stub.reset()
stub.callCount # returns 0
os.system("cd") # returns 0, which is wrong! should return None
stub.returns(5)
os.system("cd") # returns 0, which is wrong! should return 5

getCall is not working correctly

spy = sinon.spy()
spy(1)
spy(2)
spy.getCall(0)
spy.getCall(1) # fails with "IndexError: The call queue only contains 1 calls"

I think that getCall is not actually returning a call, but rather returning a queued spy/stub. Every time a new spy or stub is created, a new item is added to the queue, and this new item is what's returned by getCall. What is supposed to happen is that getCall should return the execution of a particular spy or stub. Perhaps args_list should be used by getCall?

FSM Table

Hi Kir,

I'm moving the discussion here so that it is not lost after a push. I believe that I have understood your proposal. Please find my discussion below. I'm trying to be clear, concise and kind with my writing. Please don't take any offense, because no offense is intended. :)

Kir wrote:

Based on you change, I have thought about those issues in stub for a while. And I came up a quite new design for handling them. Your design is like a Finite-state machine, and we have 2 states so far. However, it might be hard to maintain in the future without proper document.(which state have what function? what is the next state?)

Let us use the term One-Object-Per-Condition (OOPC) to describe the proposed solution in the pull request.

Let us use the term NFSM for the New Finite State Machine solution you are proposing. Reproduced below:

stub_function_fsm_table = {
    "onCall": {"returns", "throws"},
    "withArgs": {"onCall", "returns", "throws"}
}
CURRENT_STATE = ""

def fsm_control(func, *args, **kwargs):
    def inner_fsm_control():
        if CURRENT_STATE and func.__name__ in stub_function_fsm_table[CURRENT_STATE]:
            CURRENT_STATE = func.__name__
            return func(*args, **kwargs)
        else:
            raise Exception("not allowed")
    return inner_fsm_control

@fsm_control
def onCall(...):
   ...

The OOPC uses one object to hold one condition. In OOPC, if the user does not capture a newly created object by saving the object in a variable, or calling returns or throws, then the state is reset. E.g.

stub = sinon.stub()
stub.withArgs(3) # this condition is lost
stub.returns(5)  # line 3, state is reset
assert stub() == 5

OOPC resets the state in line 3, so the stub will always return 5. With NFSM, if you don't return a new object in the call to withArgs, how would you reset the state in this way? I don't believe that NFSM can meet this requirement.

There are two benefit from this design.

  1. Easy to maintain without document + Extendable + High performance O(1).
  2. Lower memory consumption (without creating new memory for new object but reference object itself)

I agree that the maintainability of NFSM would be better, since it clearly lays out the states and possible transitions. However, if it doesn't meet the requirements then a more complex solution like OOPC is required.

Regarding performance: unless 10,000 or more operations are being executed, performance is not relevant. In the case of OOPC, users will never be creating more than 20 or 30 objects. Hence, I don't see any performance issue here, in terms of time or memory consumption.

What do you think?

I hope that helps,

--Jonathan

Missing Stub Async API

There are many asynchronous Stub API functions like this one:

stub.callsArgAsync(index);
# Same as their corresponding non-Async counterparts, but with callback 
# being deferred (executed not immediately but after short timeout and in another “thread”)

I'm not sure if these are needed for Sinon.PY. What do you think?

Missing API functions related to SpyCall

The following SpyCall API functions are missing:

spy.notCalled  # true if the spy was not called

spy.calledImmediatelyBefore(anotherSpy);
# Returns true if spy was called before anotherSpy, and no spy calls occurred between spy and 
# anotherSpy.

spy.calledImmediatelyAfter(anotherSpy);
# Returns true if spy was called after anotherSpy, and no spy calls occurred between anotherSpy and spy.

spy.getCalls()  # Returns an Array of all calls recorded by the spy.

spyCall.calledWith(arg1, arg2, ...); # Returns true if call received provided arguments (and possibly others).

spyCall.calledWithExactly(arg1, arg2, ...); # Returns true if call received provided arguments and no others.

spyCall.calledWithMatch(arg1, arg2, ...);
# Returns true if call received matching arguments (and possibly others). This behaves the same as 
# spyCall.calledWith(sinon.match(arg1), sinon.match(arg2), ...).

spyCall.notCalledWith(arg1, arg2, ...); # Returns true if call did not receive provided arguments.

spyCall.notCalledWithMatch(arg1, arg2, ...);
# Returns true if call did not receive matching arguments. This behaves the same as 
# spyCall.notCalledWith(sinon.match(arg1), sinon.match(arg2), ...).

spyCall.threw(); # Returns true if call threw an exception.

spyCall.threw("TypeError"); # Returns true if call threw exception of provided type.

spyCall.threw(obj); # Returns true if call threw provided exception object.

Cannot call class stub directly

class C():
    def f2(self, x):
        return x*2
c = C()
stub = sinon.stub(c, 'f2')
stub.returns(5)
assert c.f2() == 5
assert stub() == 5 # fails, returns None

Missing Spy API functions

Here are some Spy API functions that are still missing:

spy.withArgs()
# Creates a spy that only records calls when the received arguments match those passed
# to withArgs. This is useful to be more expressive in your assertions, where you can access
# the spy with the same call.

spy.printf("format string", [arg1, arg2, ...])
# Returns the passed format string with the following replacements performed:
# %n
#     the name of the spy "spy" by default)
# %c
#     the number of times the spy was called, in words ("once", "twice", etc.)
# %C
#     a list of string representations of the calls to the spy, with each call prefixed by a newline and four
#     spaces
# %t
#     a comma-delimited list of this values the spy was called on
# %n
#     the formatted value of the nth argument passed to printf
# %*
#     a comma-delimited list of the (non-format string) arguments passed to printf 

Cannot stub a whole class

class C():
    def f2(self, x):
        return x*2
    def f3(self, x):
        return x*3
c = C()
stub = sinon.stub(c) # raises exception

args always returns all args for all calls

Hi,

The following 2 calls to args are returning the same values:

stub = sinon.stub()
stub(1, 2, 3)
stub(4, 5, 6)
print stub.getCall(0).args # [(1, 2, 3), (4, 5, 6)]
print stub.args # [(1, 2, 3), (4, 5, 6)]

The call to stub.getCall(0).args should only return (1, 2, 3).

Thanks!

Nice-to-have: stubbing constructors

It would be nice to be able to stub a constructor, such as in this code:

import urllib2
import sinon
stub = sinon.stub(urllib2, 'Request')

This kind of behaviour came for free in Sinon.JS because, in JavaScript, constructors are also functions. In Python we'd have to make a special case for handling constructors.

Get rid of target "dirty hack"

In Wrapper.py and stub.py there is a line # Todo: dirty hack related to the element conditions["target"]. This special case exists for when a new stub is created based on a class (as opposed to an object). We should find a way to remove this dirty hack.

Chained onCall and withArgs doesn't work

stub = sinon.stub()
stub.withArgs(42).onFirstCall().returns(1).onSecondCall().returns(2)
stub.returns(0);
assert stub(1) == 0
assert stub(42) == 1 # fails, returns 0
assert stub(1) == 0
assert stub(42) == 2
assert stub(1) == 0
assert stub(42) == 0

Spy API error

Hi,

First, thanks for the great API! :)

Unfortunately, there is an error in the Spy API in the following attributes:

spy.firstCall
spy.secondCall
spy.thirdCall
spy.lastCall

These are all supposed to be equivalent to calling spy.getCall(n) with the appropriate value of n, with a type of spyCall. Currently, these attributes have a Boolean value, which is incorrect.

I know it's not super well documented in the Sinon.js documentation, but if you try it out in JavaScript you will see that I am right. Also, it doesn't make sense for firstCall to have a Boolean value since that does the same thing as calledOnce.

Thanks,

Code duplication

There is code duplication in SinonStub:

  1. For withArgs/onCall
  2. For returns/throws

Missing Stub Callback API

The following Stub Callback API functions are missing:

stub.callsArg(index);
# Causes the stub to call the argument at the provided index as a callback function.
# stub.callsArg(0); causes the stub to call the first argument as a callback.

stub.callsArgWith(index, arg1, arg2, ...);
# Like callsArg, but with arguments to pass to the callback.

stub.yields([arg1, arg2, ...])
# Similar to callsArg.
# Causes the stub to call the first callback it receives with the provided arguments (if any).
# If a method accepts more than one callback, you need to use callsArg to have the stub
# invoke other callbacks than the first one.

stub.yieldsTo(property, [arg1, arg2, ...])
# Causes the spy to invoke a callback passed as a property of an object to the spy.
# Like yields, yieldsTo grabs the first matching argument, finds the callback and calls 
# it with the (optional) arguments.

Missing Stub Invocation API

The following stub invocation API functions are missing:

stub.yield([arg1, arg2, ...])
# Invoke callbacks passed to the stub with the given arguments.
# If the stub was never called with a function argument, yield throws an error.
# Also aliased as invokeCallback.

stub.yieldTo(callback, [arg1, arg2, ...])
# Invokes callbacks passed as a property of an object to the stub.
# Like yield, yieldTo grabs the first matching argument, finds the callback and 
# calls it with the (optional) arguments.

stub.callArg(argNum)
# Like yield, but with an explicit argument number specifying which callback to call.
# Useful if a function is called with more than one callback, and simply calling the
# first callback is not desired.

stub.callArgWith(argNum, [arg1, arg2, ...])
# Like callArg, but with arguments.

Different stubs are sharing arguments

stub1 = sinon.stub()
stub2 = sinon.stub()
stub1(1, 2, 3)
stub2(4, 5, 6)
print stub2.getCall(0).args # [(1, 2, 3)]
print stub2.args # [(4, 5, 6)]

The call to stub2.getCall(0).args should return (4, 5, 6). Somehow this call is returning the arguments from stub1!

Precedence of onCall vs withArgs vs returns not correct

In case of conflict between conditions, the order of precedence is:

  1. withArgs.onCall
  2. withArgs
  3. onCall
  4. No conditions
stub = sinon.stub()
stub.withArgs('A').returns('Arg of A')
stub.onFirstCall().returns('First call!')
stub.onSecondCall().returns('Second call!')
stub.returns('No args')
assert stub() == 'First call!'  # fails, returns 'No args'
assert stub('A') == 'Arg of A'
assert stub() == 'No args'

call count reset when using returns or throws

stub = sinon.stub()
stub.onFirstCall().returns('first call')
assert stub() == 'first call'
stub.onSecondCall().returns('second call')
assert stub() == 'second call' # fails, returns 'first call'

Note that this applies to throws as well

Cannot chain throws

stub = sinon.stub().onFirstCall().throws(BaseException('Hello World')).onSecondCall().returns(42)
stub() # we expect an exception 'Hello World', instead we get an error
stub() # we expect a return value of 42, instead we get an error
stub() # we expect a return value of None, instead we get an error

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.