-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
td.verify(object.function(new Class("param"))) not asserting "param" #362
Comments
Yeah, if this is all your subject does, then what you have here is the most you can do to specify the interaction you want: td.verify(new client.Security("username", "password"), {times: 1});
td.verify(client.setSecurity(td.matchers.isA(client.Security))); Since there is room for a disconnect between the two steps being verified, I added a verification option that There are two reasons why this is a pain point in the library and in faking JavaScript stuff generally:
So when greeted with this pain point, what I would do is use this as feedback to pass real value types and fake out the things with logic. In case this isn't clear, how I'd implement the same behavior given this observation follows, including new names to make it clear what does what (since it splits this // client-credentials.js
module.exports = function ClientCredentials(username, password)
{
this.username = username;
this.password = password;
} // sets-client-credentials.js
module.exports = function(security) {} // service.js
const ClientCredentials = require("./client-credentials");
const setsClientCredentials = require("./sets-client-credentials");
module.exports = {
createClient: function(username, password)
{
setsClientCredentials(new ClientCredentials(username, password));
}
}; // service-test.js
const td = require("testdouble");
const ClientCredentials = require("./client-credentials")
let setsClientCredentials, subject
describe("service", function()
{
beforeEach(function()
{
setsClientCredentials = td.replace("./sets-client-credentials");
subject = require("./service");
});
it("should set the credentials", function()
{
subject.createClient("username", "password");
td.verify(setsClientCredentials(td.matchers.argThat(function(credential){
return credential.username === 'username' &&
credential.password == 'password' &&
credential instanceof ClientCredentials
})));
});
}); You could, of course, verify this in a few different ways. You could use an argument captor instead, or you could have a more convenient way of comparing two value types. However, I wouldn't stop there! I still wouldn't find myself writing tests like this much, because cases where I need to use advanced call verification features like What I mean by this is that if I found myself writing this test, I think my next questions would be "why doesn't This is the sort of thing I mean when I say that the purpose of TDD with isolated tests is to get "design feedback" about the usability of our private APIs. It's not often obvious that a very simple API (like the one you presented above), has some issues with clarity and maybe usability, but trying to write an isolated unit test to specify those (again, very simple) interactions was so painful that it certainly encourages one to start asking questions. |
By the way, if you haven't seen it yet, a lot of my discussion above might be informed by watching my recent talk on mocking: http://blog.testdouble.com/posts/2018-03-06-please-dont-mock-me |
Thanks for an elaborate answer! |
@jasonkarns just reminded me that ES constructors can indeed specify what they return so long as it's an object And it turns out that testdouble.js actually supports this, I just always miss it because I usually try stubbing a string, which fails since it doesn't have the right prototype. This means in your initial example, you could have done something like this instead of two verifications (warning: I flushed my sample project so I'm just guessing here that this should work based on a repl experiment): const credentials = {username: 'a', password: 'b'}
td.when(new client.Security("username", "password")).thenReturn(credentials)
service.createClient("username", "password")
td.verify(client.setSecurity(credentials)); |
To be clear, a constructor (either class constructor or old-school constructor function) can return something other than the instance as long as the thing being returned is an object. So returning a primitive from a constructor will fail, but returning any old object (including plain object literals) is fine. (They don't need to be subclasses of the constructor's parent.)
(Tested above using native node. All bets are off if this is getting compiled by babel or something.) |
This did the trick :) |
Description
Verifying a function has been called with a new instance of a class with specific values passed to constructor.
Issue
I don't know how to do this and did not manage to find any example explaining this.
Environment
node 8.9.0
npm 6.0.0
testdouble 3.7.0
Example Code
N/A
Runkit Notebook
N/A
Code-fenced Examples
client.js
service.js (This is the module I want to test)
tests.js
The text was updated successfully, but these errors were encountered: