Can we consider sending a request to an endpoint a unit or an integration test?
import lib from 'testing-lib';
// ...
const { testClient, expect } = lib;
const response = testClient
.request(app)
.get('/test/endpoint/');
// ...
expect(response).fulfills.some.condition.ok
I have a feeling that this is an integration test because it will ascertain that every piece, that is in between the request going out and the response coming back, is working as expected. I need to know if my vague understanding is correct or if I am missing some details.
I'm gonna go with neither. It's a functional test.
Unit tests test units of code. Hence the name. A unit of code is typically a function, class or module of some sort.
Integration tests validate that our units of code work together as expected. But it's still just testing the code.
Functional tests test the actual software in a deployed state through exposed interfaces.
So, in Node, a unit test might be testing one of your JavaScript modules by itself and mock out the dependencies. An integration test would be testing that your modules work together and would mock out only the extreme edges of the system. And a functional test would test that a particular endpoint works over HTTP and wouldn't mock anything.
I'll add that I encourage sticking to DRY principles while writing your tests. If you have a unit test that validates a thing, you don't need to validate that in an integration test. Just validate that the units work together as expected. And the same with functional tests. Don't validate the integration, that the units work together. Validate that the endpoints map to the expected behavior.
Yeap this is more an integration test.
Unit tests are more like the functional business logic test. For an example after your route handler (controller) received the request and calls Service to handle the logic.
Test of that logic is a unit test.
Integration test is checking if data-flow works fine.
Related
What is Mocking? .
Prologue: If you look up the noun mock in the dictionary you will find that one of the definitions of the word is something made as an imitation.
Mocking is primarily used in unit testing. An object under test may have dependencies on other (complex) objects. To isolate the behaviour of the object you want to test you replace the other objects by mocks that simulate the behaviour of the real objects. This is useful if the real objects are impractical to incorporate into the unit test.
In short, mocking is creating objects that simulate the behaviour of real objects.
At times you may want to distinguish between mocking as opposed to stubbing. There may be some disagreement about this subject but my definition of a stub is a "minimal" simulated object. The stub implements just enough behaviour to allow the object under test to execute the test.
A mock is like a stub but the test will also verify that the object under test calls the mock as expected. Part of the test is verifying that the mock was used correctly.
To give an example: You can stub a database by implementing a simple in-memory structure for storing records. The object under test can then read and write records to the database stub to allow it to execute the test. This could test some behaviour of the object not related to the database and the database stub would be included just to let the test run.
If you instead want to verify that the object under test writes some specific data to the database you will have to mock the database. Your test would then incorporate assertions about what was written to the database mock.
Other answers explain what mocking is. Let me walk you through it with different examples. And believe me, it's actually far more simpler than you think.
tl;dr It's an instance of the original class. It has other data injected into so you avoid testing the injected parts and solely focus on testing the implementation details of your class/functions.
Simple example:
class Foo {
func add (num1: Int, num2: Int) -> Int { // Line A
return num1 + num2 // Line B
}
}
let unit = Foo() // unit under test
assertEqual(unit.add(1,5),6)
As you can see, I'm not testing LineA ie I'm not validating the input parameters. I'm not validating to see if num1, num2 are an Integer. I have no asserts against that.
I'm only testing to see if LineB (my implementation) given the mocked values 1 and 5 is doing as I expect.
Obviously in the real word this can become much more complex. The parameters can be a custom object like a Person, Address, or the implementation details can be more than a single +. But the logic of testing would be the same.
Non-coding Example:
Assume you're building a machine that identifies the type and brand name of electronic devices for an airport security. The machine does this by processing what it sees with its camera.
Now your manager walks in the door and asks you to unit-test it.
Then you as a developer you can either bring 1000 real objects, like a MacBook pro, Google Nexus, a banana, an iPad etc in front of it and test and see if it all works.
But you can also use mocked objects, like an identical looking MacBook pro (with no real internal parts) or a plastic banana in front of it. You can save yourself from investing in 1000 real laptops and rotting bananas.
The point is you're not trying to test if the banana is fake or not. Nor testing if the laptop is fake or not. All you're doing is testing if your machine once it sees a banana it would say not an electronic device and for a MacBook Pro it would say: Laptop, Apple. To the machine, the outcome of its detection should be the same for fake/mocked electronics and real electronics. If your machine also factored in the internals of a laptop (x-ray scan) or banana then your mocks' internals need to look the same as well. But you could also use a MacBook that no longer works.
Had your machine tested whether or not devices can power on then well you'd need real devices.
The logic mentioned above applies to unit-testing of actual code as well. That is a function should work the same with real values you get from real input (and interactions) or mocked values you inject during unit-testing. And just as how you save yourself from using a real banana or MacBook, with unit-tests (and mocking) you save yourself from having to do something that causes your server to return a status code of 500, 403, 200, etc (forcing your server to trigger 500 is only when server is down, while 200 is when server is up.
It gets difficult to run 100 network focused tests if you have to constantly wait 10 seconds between switching over server up and down). So instead you inject/mock a response with status code 500, 200, 403, etc and test your unit/function with a injected/mocked value.
Be aware:
Sometimes you don't correctly mock the actual object. Or you don't mock every possibility. E.g. your fake laptops are dark, and your machine accurately works with them, but then it doesn't work accurately with white fake laptops. Later when you ship this machine to customers they complain that it doesn't work all the time. You get random reports that it's not working. It takes you 3 months to figure out that the color of fake laptops need to be more varied so you can test your modules appropriately.
For a true coding example, your implementation may be different for status code 200 with image data returned vs 200 with image data not returned. For this reason it's good to use an IDE that provides code coverage e.g. the image below shows that your unit-tests don't ever go through the lines marked with brown.
image source
Real world coding Example:
Let's say you are writing an iOS application and have network calls.Your job is to test your application. To test/identify whether or not the network calls work as expected is NOT YOUR RESPONSIBILITY . It's another party's (server team) responsibility to test it. You must remove this (network) dependency and yet continue to test all your code that works around it.
A network call can return different status codes 404, 500, 200, 303, etc with a JSON response.
Your app is suppose to work for all of them (in case of errors, your app should throw its expected error). What you do with mocking is you create 'imaginary—similar to real' network responses (like a 200 code with a JSON file) and test your code without 'making the real network call and waiting for your network response'. You manually hardcode/return the network response for ALL kinds of network responses and see if your app is working as you expect. (you never assume/test a 200 with incorrect data, because that is not your responsibility, your responsibility is to test your app with a correct 200, or in case of a 400, 500, you test if your app throws the right error)
This creating imaginary—similar to real is known as mocking.
In order to do this, you can't use your original code (your original code doesn't have the pre-inserted responses, right?). You must add something to it, inject/insert that dummy data which isn't normally needed (or a part of your class).
So you create an instance the original class and add whatever (here being the network HTTPResponse, data OR in the case of failure, you pass the correct errorString, HTTPResponse) you need to it and then test the mocked class.
Long story short, mocking is to simplify and limit what you are testing and also make you feed what a class depends on. In this example you avoid testing the network calls themselves, and instead test whether or not your app works as you expect with the injected outputs/responses —— by mocking classes
Needless to say, you test each network response separately.
Now a question that I always had in my mind was: The contracts/end points and basically the JSON response of my APIs get updated constantly. How can I write unit tests which take this into consideration?
To elaborate more on this: let’s say model requires a key/field named username. You test this and your test passes.
2 weeks later backend changes the key's name to id. Your tests still passes. right? or not?
Is it the backend developer’s responsibility to update the mocks. Should it be part of our agreement that they provide updated mocks?
The answer to the above issue is that: unit tests + your development process as a client-side developer should/would catch outdated mocked response. If you ask me how? well the answer is:
Our actual app would fail (or not fail yet not have the desired behavior) without using updated APIs...hence if that fails...we will make changes on our development code. Which again leads to our tests failing....which we’ll have to correct it. (Actually if we are to do the TDD process correctly we are to not write any code about the field unless we write the test for it...and see it fail and then go and write the actual development code for it.)
This all means that backend doesn’t have to say: “hey we updated the mocks”...it eventually happens through your code development/debugging. ّBecause it’s all part of the development process! Though if backend provides the mocked response for you then it's easier.
My whole point on this is that (if you can’t automate getting updated mocked API response then) human interaction is likely required ie manual updates of JSONs and having short meetings to make sure their values are up to date will become part of your process
This section was written thanks to a slack discussion in our CocoaHead meetup group
Confusion:
It took me a while to not get confused between 'unit test for a class' and the 'stubs/mocks of a class'.
E.g. in our codebase we have:
class Device
class DeviceTests
class MockDevice
class DeviceManager
class Device is the actual class itself.
class DeviceTests is where we write unit-tests for the Device class
class MockDevice is a mock class of Device. We use it only for the purpose of testing. e.g. if our DeviceManager needs to get unit-tested then we need dummy/mock instances of the Device class. The MockDevice can be used to fulfill the need of dummy/mock instances.
tldr you use mock classes/objects to test other objects. You don't use mock objects to test themselves.
For iOS devs only:
A very good example of mocking is this Practical Protocol-Oriented talk by Natasha Muraschev. Just skip to minute 18:30, though the slides may become out of sync with the actual video 🤷♂️
I really like this part from the transcript:
Because this is testing...we do want to make sure that the get function
from the Gettable is called, because it can return and the function
could theoretically assign an array of food items from anywhere. We
need to make sure that it is called;
There are plenty of answers on SO and good posts on the web about mocking. One place that you might want to start looking is the post by Martin Fowler Mocks Aren't Stubs where he discusses a lot of the ideas of mocking.
In one paragraph - Mocking is one particlar technique to allow testing of a unit of code with out being reliant upon dependencies. In general, what differentiates mocking from other methods is that mock objects used to replace code dependencies will allow expectations to be set - a mock object will know how it is meant to be called by your code and how to respond.
Your original question mentioned TypeMock, so I've left my answer to that below:
TypeMock is the name of a commercial mocking framework.
It offers all the features of the free mocking frameworks like RhinoMocks and Moq, plus some more powerful options.
Whether or not you need TypeMock is highly debatable - you can do most mocking you would ever want with free mocking libraries, and many argue that the abilities offered by TypeMock will often lead you away from well encapsulated design.
As another answer stated 'TypeMocking' is not actually a defined concept, but could be taken to mean the type of mocking that TypeMock offers, using the CLR profiler to intercept .Net calls at runtime, giving much greater ability to fake objects (not requirements such as needing interfaces or virtual methods).
Mock is a method/object that simulates the behavior of a real method/object in controlled ways. Mock objects are used in unit testing.
Often a method under a test calls other external services or methods within it. These are called dependencies. Once mocked, the dependencies behave the way we defined them.
With the dependencies being controlled by mocks, we can easily test the behavior of the method that we coded. This is Unit testing.
What is the purpose of mock objects?
Mocks vs stubs
Unit tests vs Functional tests
Mocking is generating pseudo-objects that simulate real objects behaviour for tests
The purpose of mocking types is to sever dependencies in order to isolate the test to a specific unit. Stubs are simple surrogates, while mocks are surrogates that can verify usage. A mocking framework is a tool that will help you generate stubs and mocks.
EDIT: Since the original wording mention "type mocking" I got the impression that this related to TypeMock. In my experience the general term is just "mocking". Please feel free to disregard the below info specifically on TypeMock.
TypeMock Isolator differs from most other mocking framework in that it works my modifying IL on the fly. That allows it to mock types and instances that most other frameworks cannot mock. To mock these types/instances with other frameworks you must provide your own abstractions and mock these.
TypeMock offers great flexibility at the expense of a clean runtime environment. As a side effect of the way TypeMock achieves its results you will sometimes get very strange results when using TypeMock.
I would think the use of the TypeMock isolator mocking framework would be TypeMocking.
It is a tool that generates mocks for use in unit tests, without the need to write your code with IoC in mind.
If your mock involves a network request, another alternative is to have a real test server to hit. You can use a service to generate a request and response for your testing.
For my end-to-end testing, I am using Puppeteer with Jest. My codebase is large and I have a lot of tests at the moment. There is one 'preparatory' test suite which checks if there actually is sufficient data on our page for the rest of the tests to proceed. I would like to force this test to run first and then terminate the Jest process if it fails, since there is no need for other tests to run after that.
The --runInBand flag not only has a hard performance hit due to the large number of tsts but also requires manual termination of the Jest process after the failure of the first test, which I have not been able to do.
What is the best way to achieve the above? Could I please get a minimal example of the solution? Thanks!
One of places that precede all tests is globalSetup. It runs in parent process and doesn't obtain Jest environment, so the test needs to be set up manually:
import expect from 'expect';
export default async () => {
// assertions go here
};
What is the point of mocking the method of an external library when unit testing?
Let's say that I have a function that uses XYZ library to get the current user using a given authorization token, and then if it finds the user, it returns a valid AWS policy:
export const getUser = async token => {
const user = await XYZ.getUser(token)
return {
// valid policy
context: user,
}
}
If an invalid token is given, the getUser should throw an error.
What is the point of testing this function if the XYZ.getUser is already well tested?
Why mock the getUser instead of using the real one?
You wouldn't want to invoke the actual HTTP request for XYZ.getUser(token), due to the following reasons.
1) The objective of unit testing is to test a specific behaviour, and in your very case, it is to test that your getUser function actually works.
2) You wouldn't want to make an actual request to the backend, as that would usually mean writing/reading/deleting something from the database. In many cases, it is not a good idea to overcomplicate your unit testing, as such operations should not be tested on the front-end, since you would want to limit and simplify your unit testing to test for a very specific behaviour.
3) Since the main objective is to test the above behaviour, you would want to simplify and limit what you are testing. As such, you will need to understand that unit testing of network requests of XYZ.getUser(token) should be done at the server-side, instead of client-side (or front-end).
4) By mocking the response from XYZ.getUser(token), we can simulate multiple scenarios and responses, such as successful responses (Code 200 OK), and other types of errors (business logic errors from the backend, or network errors such as error 400, 500, etc). You can do so by hardcoding the required JSON responses from XYZ.getUser(token) on the different types of scenarios you need to test.
this goes back to the definition and usages of unit tests.
Unit tests are supposed to test well defined functionality, for example you might write an algorithm and the functionality of that is very well defined. Then, because you know how it's supposed to function then you would unit test that.
Unit tests by definition, are not meant to touch any external systems, such as but not limited to files, APIs, databases, anything that requires going outside of the logical unit which is the code you want to test.
There are multiple reasons for that among which is execution speed. Calling an API, or using a third party call, takes a while. That's not necessary a problem when you look at one test, but when you have 500 tests or more then the delays will be important. The main idea is that you are supposed to run your unit tests all the time, very quickly, you get immediate feedback, is anything broken, have I broken something in other parts of the system. In any mature system you will run these tests as part of your CI/CD process and you cannot afford to wait too long for them to finish. no, they need to run in seconds, which is why you have the rules of what they should or should not touch.
You mock external things, to improve execution speed and eliminate third party issues as you really only want to test your own code.
What if you call a third party API and they are down for maintenance? Does that mean you can't test your code because of them?
Now, on the topic of mocking, do not abuse it too much, if you code in a functional way then you can eliminate the need for a lot of mocking. Instead of passing or har-coding dependencies, pass data. This makes it very easy to alter the input data without mocking much. You test the system as a while using integration / e2e tests, this gives you the certainty that your dependencies are resolved correctly.
I recently watched a number of talks from the AssertJS conference (which I highly recommend), among them #kentcdodds "Write Tests, Not Too Many, Mostly Integration." I've been working on an Angular project for over a year, have written some unit tests, and just started playing with Cypress, but I still feel this frustration around integration tests, and where to draw the lines. I'd really love to talk to some pro who does this day in and day out, but I don't know any where I work. Since I'm tired of not being able to figure this out, I thought I'd just ask the world here, cause you all are fantastic.
So in Angular (or React or Vue, etc), you have component code, and then you have the HTML template, and usually they interact in some way. The component code has functions in it that can be unit tested, and that part I'm ok with.
Where I haven't gotten things straight in my mind is, do you call it an integration test when you're testing how a component function changes the UI? If you're testing that kind of thing, should that be done just in E2E tests? Because Angular/Jasmine(or Jest) lets you do this kind of thing, referencing the UI:
const el = fixture.debugElement.queryAll(By.css('button'));
expect(el[0].nativeElement.textContent).toEqual('Submit')
But does that mean you should? And if you do, then do you not cover that in your E2E tests?
And regarding integration with things like services, how far do you go with integrating? If you mock the actual HTTP call, and just test that it would get called with the right functions, is that an integration test, or is it still a unit test?
To sum up, I intuitively know what I need to test to have confidence that things are working as they should, I'm just not sure how to discern when something requires all three kinds of tests or not.
I know this is getting long, but here's my example app:
There's a property called hasNoProducts that is set after a product is chosen and data is returned from the server (or not if there is none). If hasNoProducts is true, UI (through an *ngIf) shows that "Sorry" message. If false, then other selections become available. Depending on the product picked, those options change.
So I know I can write a unit test and mock the HTTP request so that I can test that hasNoProducts is set correctly. But then, I want to test that the message is displayed, or that the additional options are displayed. And if there is data, test that switching the product changes the data in the other lists that would subsequently show on screen. If I do that using Angular/Jasmine, is it an integration test since I'm "integrating" component and template? If not, then what would be an integration test?
I could keep asking questions, but I'll stop there in the hopes that someone has read this far and has some insight. Again, I've read tons of articles, watched tons of videos and done tutorials, but every time I sit down to apply to a real project, I get stuck on things like this, and I want to get past this! Thanks in advance.
What distinguishes unit-tests and integration-tests (and then subsystem-tests and system-tests) is the goal that you want to achieve with the tests.
The goal of unit-testing is to find those bugs in small pieces of code that can be found if these pieces of code are isolated. Note that this does not mean you truly must isolate the code, but it means your focus is the isolated code. In unit-testing, mocking is very common, since it allows to stimulate scenarios that otherwise are hard to test, or it speeds up build and execution times etc., but mocking is not mandatory: For example, you would not mock calls to a mathematical sin() function from the standard library, because the sin() function does not keep you from reaching your testing goals. But, leaving the sin() function in does not turn these tests into integration tests. Strictly speaking, you could even have unit-tests where some real network accesses take place (if you are too lazy to mock the network access), but due to the non-determinism, delays etc. these unit tests would be slow and unreliable, which means they would simply not be well suited to specifically find the bugs in the isolated code. That's why everybody says that "if there is some real network access, it is not a unit-test", which is not formally but practically correct.
Since in unit-testing you intentionally only focus on the isolated code, you will not find bugs that are due to misunderstandings about interactions with other components. If you mock some depended-on-component, then you implement these mocks based on your understanding of how the other component behaves. If your understanding is wrong, your mock implementations will reflect your wrong understanding, and your unit-tests will succeed, although in the integrated system things will break. That is not a flaw of unit-testing, but simply the reason why there are other test levels like integration testing. In other words, even if you do unit-testing perfectly, there will unavoidably remain some bugs that unit-testing is not even intending to find.
Now, what are integration tests then? They are defined by the goal to find bugs in the interactions between (already tested) components. Such bugs can, for example, be due to mutual misconceptions of the developers of the components about how an interface is meant to work. For example, in the case of a library component B that is used from A: Does A call functions from the right component B (rather than from C), do the calls happen while B is already in a proper state (B might not be initialized yet or in error state), do the calls happen in the proper order, are the arguments provided in the correct order and have values in the expected form (e.g. zero based index vs. one based index?, null allowed?), are the return values provided in the expected form (returned error code vs. exception) and have values in the expected form? This is for one integration scenario - there are many others, like, components exchanging data via files (binary or text? which end-of-line marker: unix, dos, ...?, ...).
There are many possible interaction errors. To find them, in integration testing you integrate the real components (the real A and the real B, no mocks, but possibly mocks for other components) and stimulate them such that the different interactions actually take place - ideally in all interesting ways, like, trying to force some boundary cases in the interaction (exchanged file is empty, ...). Again, just the fact that the test operates on a software where some components are integrated does not make it an integration test: Only if the test is specifically designed to initiate interactions such that bugs in these interactions become apparent, then it is an integration test.
Subsystem tests (which are the next level) then focus, again, on the remaining bugs, that is, those bugs which neither unit-testing nor integration testing intend to find. Examples are requirements on the component C that were not considered when C was decomposed into A and B, or, if C is built using some outdated version of A where some bug was still in. However, when climbing up from unit-testing via integration testing to subsystem testing and above, it is a challenge to stay focused: Only to have tests for bugs that could not have been found before, and not to, say, repeat unit-tests on subsystem level.
Let's say I have the following method:
function sendUserDataToServer(userData) {
// Do some configuration stuff
$.ajax('SomeEndpoint', userData);
}
When writing a unit test for this function, I could go one of two ways:
Create a spy around $.ajax and check that it was called with the expected parameters. An actual XHR request will not be sent.
Intercept the ajax request with a library like SinonJs and check the XHR object to make sure it was configured correctly.
Why I might go with option 1: It separates the functionality of $.ajax from my code. If $.ajax breaks due to a bug in jQuery, it won't create a false-negative in my unit test results.
Why I might go with option 2: If I decide I want to use another library besides jQuery to send XHRs, I won't have to change my unit tests to check for a different method. However, if there's a bug in that library, my unit tests will fail and I won't necessarily know it's the library and not my actual code right away.
Which approach is correct?
Your first option is correct. The purpose of your unit test is twofold
to verify your code contract
to verify that it communicates with other things it uses.
Your question is about the interaction with $.ajax, so the purpose of this test is to confirm it can talk correctly to its collaborators.
If you go with option 1, this is exactly what you are doing. If you go with option 2, you're testing the third party library and not that you're executing the communication protocol correctly.
You should make both tests though. The second test is probably fast enough to live with your unit tests, even though some people might call it an integration test. This test tells you that you're using the library correctly to create the object you intended to create. having both will let you differentiate between a bug in your code and the library, which will come in handy if there is a library bug that causes something else in your system to fail.
My opinion: option 1 is more correct within unit tests.
A unit test should test the unit of code only. In your case this would be the JavaScript under your control (e.g. logic within the function, within a class or namespace depending on the context of your function).
Your unit tests should mock the jQuery call and trust that it works correctly. Doing real AJAX calls is fine, but these would be part of integration tests.
See this answer regarding what is and what isn't a unit test.
As an aside, a third way would be to do the AJAX calls via a custom proxy class which can be mocked in tests but also allows you to switch the library you use.
I would use option 1 but go with the one that makes the tests easiest to understand.
You are depending on jQuery in your code, so you don't need to worry about it actually working as you point out. I have found that it makes the tests cleaner as you specify your parameters and the response and have your code process it. You can also specify which callback should be used (success or error) easily.
As for changing the library from jQuery. If you change the code your test should fail. You could argue that your behavior is now different. Yes, you are making an Ajax call but with a different dependency. And your test should be updated to reflect this change.
Both approaches are really correct. If you are creating your own wrapper for sending Ajax calls, it might make sense to intercept the XHR request or manually creating the request and sending it yourself. Choose the option that makes your test easiest to understand and maintain.
the most important thing is to test your own code. separate business logic from the IO code and your business logic. after that you should have methods without any logic that do the actual IO (exactly as in your example). and now: should you test that method? it depends. tests are to make you feel safer.
if you really worry that jQuery can fail and you can't afford such unlikely scenario than test it. but not with some mocking library but do full end-to-end integration tests
if you're not sure if you are using jQuery correctly then do the 'learning tests'. pass a parameters to it and check what kind of request it produces. mocking tests should be enough in this situation
if you need a documentation of a contract between frontend and backend then you may choose if you prefer to test if backend meets the contract, frontend or both
but if none of above applies to you then isn't it a waste of time to test external, well established framework? jquery guys for sure have tested it. still you will probably need some kind of integration tests (automatic or manual) to check if your frontend interacts with backend properly. and those tests will cover the network layer