Does anyone use unit test cases with Jest to ensur...
# suitescript
s
Does anyone use unit test cases with Jest to ensure script quality and reduce bugs? Curious to know what practices are generally followed to maintain development quality and minimize issues.
a
I don't use Jest personally, but I would probably start here https://stoic.software/articles/testing-mocks/ @erictgrubaugh (author) might have other details to share
thankyou 1
❤️ 2
s
we use unit tests - though admittedly only sometimes. However, we use NFT which in turn makes writing unit tests easier.
a
you use blockchain to make writing tests... easier? 😂
😂 3
s
One reason is because NFT represents records as plain class instances, so you can stub with just a POJO, unlike vanilla suitescript which requires extensive mocking effort.
a
k i was joking, but what is NFT, I'm assuming its not actually non-fungible token in this context
s
https://www.npmjs.com/package/netsuite-fasttrack-toolkit-ss2. For the record, this NFT predates the use of the acronym you're referring to 🙂
🙌 3
c
You can separate your business logic from the netsuite logic and use Jest to only test the buisness logic and not have to mock any netsuite stuff. I really don't see the point in testing if a netsuite call is made for most logic as testing that part is on netsuite and not you. If your business logic is correct, then that's all you need to be testing. You can also wrap the netsuite function calls and mock those rather than writing mocks for actual netsuite calls. There's several ways to do testing it just depends on how you want to approach it.
☝️ 1
thankyou 1
s
Right, it just so happens NFT is already providing that thin layer of abstraction that makes writing tests simple without mocking.
so you can unit test even if you're not thinking too hard about isolating the business logic 🙂
c
Yeah that sounds right. With any wrapper you can test easier. But really I would wonder "why" is anyone testing that a netsuite method was called? The real logic is in the business side and then netsuite is just getting or setting fields. Like are people really mocking record.setValue(...) and making sure its called?
i'm sure there's probably a few valid cases for it
s
I suspect a lot of code has mutation events within - e.g. they update some fields then
.save()
on it and want a unit test to ensure the .save() happens, or at a minimum they have to mock that
.save()
so the test including such a line of code can execute.
you can't have any code that contains a
record.setValue()
without mocking
record.setValue()
lest it explode if run in a unit test.
Since with NFT your business logic is often just reading/writing properties on objects, there is no mocking needed, unless a function is also working with other
N/*
modules within.
basically, the same old story of one must mock all dependencies to create a unit test. NFT naturally removes a bunch of dependencies as it relates to
N/record
so leapfrogs that whole problem.
my argument is IF one is manually abstracting away N/record calls in a similar fashion, then they are likely just reimplementing an inferior NFT :)
c
The way i look at it is there should be a method that says "shouldISave(...)" and which is dependent on business logic. You can test whether or not that save is going to be called based on your business logic with something like a "should save" test. The fact NetSuite calls .save on a record is irrelevant and nothing you should be testing as its up to netsuite to actually handle that. Basically you are creating an extra test for no gain. Yeah you gotta mock everything w/ netsuite's framework or wrap it.
I don't really expect anyone to wrap all of the netsuite methods. It's more of a last resort type thing. Separating your business logic from netsuite logic is the main key IMO.
s
aye, then your
shouldSave()
method must not call any other netsuite APIs (including getValue/setValue/sublist/subrecord) else you're back in mocking land.
c
correct
When I say business logic, that's logic independent of netsuite
s
while I totally agree that one is not testing that e.g.
save()
works as expected (we're not testing NetSuite's code) you very certainly can have business logic that depending on rules either decides to
save()
or not save.... having a unit test that
save()
was or was not called is useful, and imho simpler/cleaner than a separate
shouldSave()
function in isolation
Copy code
test('setCustomerCompanyName', function () {

    // create a 'stub' Customer with only a few fields populated (those fields we need for the test)
    const x = stub<Customer>({
      firstname: 'joe',
      lastname: 'schmoe',
      save: jest.fn()
    })
    const result = sut.createCompanyName(x)

    expect(result.companyname).toBeTruthy()
    expect(result.companyname).toEqual('joe schmoe')
    expect(x.save).toHaveBeenCalled()
  })
c
I mean that makes sense. I'm kind of of the opinion that I would leave that to blackbox testing. If you're looking for a certain coverage % then you'll maybe have to test the netsuite flows too.
There's definitely a ton of ways to approach it
s
my point is with NFT that simple test above lets you actually test business logic which includes a save without ever mocking anything. Here's the
createCompanyName()
implemenation:
c
well that save is mocked to a blank function looks like
s
// just an example function to demo unit testing export function createCompanyName (c: Customer) { c.companyname =
${c.firstname} ${c.lastname}
c.save() // see unit tests for inspecting calls to NSDAL methods like save() return c }
c
There's no logic behind the save either just that it combined the 2 names and saved. Do you gain anything by adding a test that just says that save was called?
s
I tend to agree that writing pure functions (i.e. that don't include the .save()) is good for unit testing, though in practice I find people usually mix things together within functions, and for simple code I can't really blame them.
c
For example, if I had createCompanyName(...) and then a setCompanyName(...), my createCompanyName(...) would take in the 2 params for names and I wouldn't write tests that went out and got the names. I'd probably wrap it in a "getCustomerData" method. With setCompanyName(...) I wouldn't test that .setValue(...) was called just that setCompanyName was called if we are testing the netsuite flow.
That's definitely a coding problem I see often. Methods should do 1 thing and 1 thing only but sometimes for performance that fails apart.
s
well, ultimately you do want to know that the 'companyname' property was indeed set and set correctly, do you not? we're not testing how netsuite achieves that but a test and implementation like shown above is super easy for people as there's no additional layers/functions to look to and understand
c
I would say no. The fact netsuite did it's job isn't something I would not test w/ code.
You can say for 100% certainty that you gave it the correct info. If it didn't set it correctly, that's netsuite's issue.
You're mocking stuff anyways so you'll never know even w/ a unit test
s
exactly, my test above isn't testing netsuite's behavior - it's simply testing that the field got set as desired without any additional functions or logic - i.e. no 'createCompanyName()` needed when it's trivial. I do think there's plenty of complex business logic cases where it makes sense to decompose the logic into separate functions, but not here.
c
Right I agree w/ you. We are def on the same page it's just whether or not you leave some stuff to blackbox or if you code a test for it. Good example of multiple ways to approach it. Both work but depends on what you are trying to achieve.
s
put another way, the fact that NFT represents records as plain class instances means you can pass them to your business logic functions - and write units tests on them directly as shown above, without the need to mock
getValue(), setValue()
and similar, and also without any additional overhead like functions written just to support testability.
c
The fact you even put code into functions is better than like 90% of the code I run across
💯 1
Yeah that test was super easy to read and understand for sure
If you're (in general) going to unit test, most likely your understanding of how to write code is going to have to change some to separate logic consciously. Like passing in params rather than having the method get values from netsuite. It's a process.
s
a notable characteristic of the test and implementation example I show above is NO knowledge of NetSuite is even needed - it's just javascript 101.
c
Yeah it does seem that way
s
Thankyou all for all these inputs