I'm writing test for firebase database onWrite trigger function in offline mode using stubbing as suggested in the docs and the github sample.
But I'm confused how to stub multiple .child().val() for value in my data coming in the trigger. Here is my progress to create data object:
let data={
before: {
val : ()=> null,
},
after:{
val: ()=> {/*after data here */},
child: {
"child1": "How have value here from val",
"child2": "How have value here from val"
}
}
}
Since my function is a bit complex so there are lots of value reads from the incoming data (2 to 3 level nesting) and then there are multiple access to database as well.
So is there any easy way to stub all these calls ? Or is the stubbing is only good for functions which have simple data and less read access to database?
P.S: I used online mode testing as well but that changes the state of the database which I cannot restore then
Testing functions with firestore is not easy. I have used a lot of energy to try different patterns, and ended up with this strategy.
Create generic methods in your project, for extracting data. For example a function called collectionQuery, docQuery etc.
Initialise the functions test in online mode. (this way you tests will not endless complain about things not being stubbed and mocked :))
Now in your tests, you can simply spy on your functions from step 1. If you create the functions, so that they return values, it becomes really easy.
Example of collectionQuery function (typescript):
/**
* The purpose of this relatively simple function is to query firestore!
* By wrapping the operation in a helper function, it becomes much easier to test.
* #export
* #template T Provide the variable type for the function
* #param {FirebaseFirestore.Firestore} afs The firestore instance, usefull for switching between functions/admin
* #param {FirebaseFirestore.Query} query The query to perform. By accepting a query, it becomes very flexpible
* #returns {Promise<Array<T>>} The result as an array
*/
export async function collectionQuery<T>(afs: FirebaseFirestore.Firestore, query: FirebaseFirestore.Query): Promise<Array<T>> {
try {
if (afs == null || query == null) {
throw new Error(`You must provide an firestore instance, or a query`);
}
const snapshot = await query.get();
const returnData: T[] = snapshot.docs.map((doc) => doc.data() as T);
return returnData;
} catch (error) {
throw new Error(`collectionQuery failed unexpected with: ${error.message}`);
}
}
The benefit is that in a unit test, I can now simply spy on this function. I am using Jest, so something like this:
jest.spyOn(firehelpers, 'collectionQuery').mockReturnValue([]} // simulate no data returned by query
jest.spyOn(firehelpers, 'collectionQuery').mockReturnValue([{foo: 'bar'}]} // One result returned
This strategy makes it easy to test functions that read and write to the db/firestore. Could also be used in combination with stubbing on the firebase libraries if needed.
Let me know if it helps :)
Related
All the examples of jest and jest-fetch-mock I have come across use functions that perform an API query and return a payload directly from the function call.
In my case, I have a different setup. I have a class that has a property called 'data'. In the class there is a method called "get" which pulls data using fetch API and stores it in the data property. When the method is called, it simply returns true or false based on promise resolve or reject.
I am trying to figure out how to write unit tests for this in this case. My function doesn't return the data fetched; only a boolean value.
So if I use jestSpyOn to mock the class method, how would I set the data property, and then retrieve the result?
In my code, I do something like this (NOT in testing, but in the actual app):
contactStore = new ContactListStore();
// 'all' is a sample param passed
contactStore.get('all').then(res => {
if(res){
...perform action
}
});
As you can see the res argument is only boolean, and if true, then contactStore.data will contain the information retrieved from the server.
So to run a unit test on it, I need to call a mock get, and set a mock data property.
Any ideas how this would be done?
In your mock method, you just need return true.
Here is part of the code which extends BaseEntityManager:
namespace Vop\PolicyBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Persistence\ObjectRepository;
use Sonata\CoreBundle\Model\BaseEntityManager;
class AdditionalInsuredTypeManager extends BaseEntityManager
{
/**
* #param int $productId
*
* #return ArrayCollection
*/
public function getProductInsuredTypes($productId = null)
{
$repository = $this->getRepository();
$allActiveTypes = $repository->findAllActive();
// other code
}
/**
* #return AdditionalInsuredTypeRepository|ObjectRepository
*/
protected function getRepository()
{
return parent::getRepository();
}
}
And here I am trying to write a unit test:
public function testGetProductInsuredTypes()
{
$managerRegistry = $this->getMockBuilder(\Doctrine\Common\Persistence\ManagerRegistry::class)
->getMock();
$additionalInsuredTypeManager = new AdditionalInsuredTypeManager(
AdditionalInsuredTypeManager::class,
$managerRegistry
);
$additionalInsuredTypeManager->getProductInsuredTypes(null);
}
What are the problems:
I am mocking ManagerRegistry, but I have learned that I should not mock what I do not own. But this is required parameter for constructor.
I am getting error:
Unable to find the mapping information for the class Vop\PolicyBundle\Entity\AdditionalInsuredTypeManager. Please check the 'auto_mapping' option (http://symfony.com/doc/current/reference/configuration/doctrine.html#configuration-overview) or add the bundle to the 'mappings' section in the doctrine configuration.
/home/darius/PhpstormProjects/vop/vendor/sonata-project/core-bundle/Model/BaseManager.php:54
/home/darius/PhpstormProjects/vop/vendor/sonata-project/core-bundle/Model/BaseManager.php:153
/home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Entity/AdditionalInsuredTypeManager.php:46
/home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Entity/AdditionalInsuredTypeManager.php:21
/home/darius/PhpstormProjects/vop/src/Vop/PolicyBundle/Tests/Unit/Entity/AdditionalInsuredTypeManagerTest.php:22
I do not know how to fix this error, but this really has to do something with extending that BaseEntityManager I assume.
I see the error is caused by this line:
$repository = $this->getRepository();
I cannot even inject the repository from the constructor, because parent constructor has no such parameter.
There is very little amout of information about testing:
https://sonata-project.org/bundles/core/master/doc/reference/testing.html
I can not tell you whether it's useful to test your repositories, neither can I give you pointers for your error other than that you should most likely not extend doctrine's entity manager. If anything use a custom EntityRepository or write a service in which you inject the EntityManager (or better the EntityRegistry):
class MyEntityManager
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function getProductInsuredTypes($productId = null)
{
$repository = $this->entityManager->getRepository(Product::class);
$allActiveTypes = $repository->findAllActive();
// other code
}
}
What I can give you is an explanation how I approach testing repositories:
I think unit tests, especially with mocks, seem a bit wasteful. They only tie you to the current implementation and since anything of relevance is mocked out you will most likely not test any behavior.
What might be useful is doing functional tests where you provide a real database connection and then perform a query against the repository to see if it really returns the expected results. I usually do this for more complex queries or when using advanced doctrine features like native queries with a custom ResultsetMap. In this case you would not mock the EntityManager and instead would use Doctrine's TestHelper to create an in memory sqlite-database. This can look something like this:
protected $entityManager;
protected function setUp()
{
parent::setUp();
$config = DoctrineTestHelper::createTestConfiguration();
$config->setNamingStrategy(new UnderscoreNamingStrategy());
$config->setRepositoryFactory(new RepositoryFactory());
$this->entityManager = DoctrineTestHelper::createTestEntityManager($config);
}
The downside is, that you will manually have to register custom types and listeners which means the behavior might differ from your production configuration. Additionally you are still tasked with setting up the schema and providing fixtures for your tests. You will also most likely not use SQLite in production so this is another deviation. The benefit is that you will not have to deal with clearing your database between runs and can easily parallelize running tests, plus it's usually faster and easier than setting up a complete test database.
The final option is somewhat close to the previous one. You can have a test database, which you define in your parameters, environment variables or a config_test.yml, you can then bootstrap the kernel and fetch the entity manager from your DI-container. Symfony's WebTestCase can be used as a reference for this approach.
The downside is, that you will have to boot your kernel and make sure to have separate database setups for development, production and testing to ensure your test data does not mess up anything. You will also have to setup your schema and fixtures and additionally you can easily run into issues where tests are not isolated and start being flakey, e.g. when run in different order or when running only parts of your test suite. Obviously since this is a full integration test through your bootstrapped application the performance footprint compared to a unit test or smaller functional test is noticeably higher and application caches might give you additional headaches.
As a rule of thumb:
I trust Doctrine when doing the most basic kind of queries and therefore do not test repositories for simple find-methods.
When I write critical queries I prefer testing them indirectly on higher layers, e.g. ensure in an acceptance test that the page displays the information.
When I encounter issues with repositories or need tests on lower layers I start with functional tests and then move on to integration tests for repositories.
tl;dr
Unit tests for repositories are mostly pointless (in my opinion)
Functional tests are good for testing simple queries in isolation with a reasonable amount of effort, should you need them.
Integration tests ensure the most production like behavior, but are more painful to setup and maintain
How do I write a mock test that allows me to validate that an inaccessible property (debugLog) is set to true? Do I try to find a way to find the value of the property? Do I verify that console.debug is set? Does a spy make sense in this situation or should I use a stub?
Class X
let showDebugLogs = false,
debugLog = _.noop
/**
* Configures Class X instances to output or not output debug logs.
* #param {Boolean} state The state.
*/
exports.showDebugLogs = function (state) {
showDebugLogs = state;
debugLog = showDebugLogs ? console.debug || console.log : _.noop;
};
Unit Test
describe('showDebugLogs(state)', function () {
let spy;
it('should configure RealtimeEvents instances to output or not output debug logs', function () {
spy = sinon.spy(X, 'debugLog');
X.showDebugLogs(true);
assert.strictEqual(spy.calledOnce, true, 'Debug logging was not enabled as expected.');
spy.restore();
});
});
Mock testing is used for "isoloting" a class under test from its environment to decrease its side effects and to increase its test-ability. For example, if you are testing a class which makes AJAX calls to a web server, you'd probably do not want to:
1) wait for AJAX calls to complete (waste of time)
2) observe your tests fall apart because of possible networking problems
3) cause data modifications on the server side
and so on.
So what you do is to "MOCK" the part of your code, which makes the AJAX call, and depending on your test you either:
1) return success and response accompanying a successful request
2) return an error and report the nature of the point of failure to see how your code is handing it.
For your case, what you need is just a simple unit test case. You can use introspection techniques to assert internal states of your object, if this is what you really want to. However, this comes with a warning: it is discouraged. Please see Notes at the bottom
Unit testing should be done to test behavior or public state of an object. So, you should really NOT care about internals of a class.
Therefore, I suggest you to re-consider what you are trying to test and find a better way of testing it.
Suggestion: Instead of checking a flag in your class, you can mock up logger for your test. And write at least two test cases as follows:
1) When showDebugLogs = true, make sure log statement of your mock logger is fired
2) When showDebuLogs = false, log statement of your mock logger is not called.
Notes: There has been a long debate between two schools of people: a group advocating that private members/methods are implementation details and should NOT be tested directly, and another group which opposes this idea:
Excerpt from a wikipedia article:
There is some debate among practitioners of TDD, documented in their
blogs and other writings, as to whether it is wise to test private
methods and data anyway. Some argue that private members are a mere
implementation detail that may change, and should be allowed to do so
without breaking numbers of tests. Thus it should be sufficient to
test any class through its public interface or through its subclass
interface, which some languages call the "protected" interface.[29]
Others say that crucial aspects of functionality may be implemented in
private methods and testing them directly offers advantage of smaller
and more direct unit tests
First up, where my knowledge is at:
Unit Tests are those which test a small piece of code (single methods, mostly).
Integration Tests are those which test the interaction between multiple areas of code (which hopefully already have their own Unit Tests). Sometimes, parts of the code under test requires other code to act in a particular way. This is where Mocks & Stubs come in. So, we mock/stub out a part of the code to perform very specifically. This allows our Integration Test to run predictably without side effects.
All tests should be able to be run stand-alone without data sharing. If data sharing is necessary, this is a sign the system isn't decoupled enough.
Next up, the situation I am facing:
When interacting with an external API (specifically, a RESTful API that will modify live data with a POST request), I understand we can (should?) mock out the interaction with that API (more eloquently stated in this answer) for an Integration Test. I also understand we can Unit Test the individual components of interacting with that API (constructing the request, parsing the result, throwing errors, etc). What I don't get is how to actually go about this.
So, finally: My question(s).
How do I test my interaction with an external API that has side effects?
A perfect example is Google's Content API for shopping. To be able to perform the task at hand, it requires a decent amount of prep work, then performing the actual request, then analysing the return value. Some of this is without any 'sandbox' environment.
The code to do this generally has quite a few layers of abstraction, something like:
<?php
class Request
{
public function setUrl(..){ /* ... */ }
public function setData(..){ /* ... */ }
public function setHeaders(..){ /* ... */ }
public function execute(..){
// Do some CURL request or some-such
}
public function wasSuccessful(){
// some test to see if the CURL request was successful
}
}
class GoogleAPIRequest
{
private $request;
abstract protected function getUrl();
abstract protected function getData();
public function __construct() {
$this->request = new Request();
$this->request->setUrl($this->getUrl());
$this->request->setData($this->getData());
$this->request->setHeaders($this->getHeaders());
}
public function doRequest() {
$this->request->execute();
}
public function wasSuccessful() {
return ($this->request->wasSuccessful() && $this->parseResult());
}
private function parseResult() {
// return false when result can't be parsed
}
protected function getHeaders() {
// return some GoogleAPI specific headers
}
}
class CreateSubAccountRequest extends GoogleAPIRequest
{
private $dataObject;
public function __construct($dataObject) {
parent::__construct();
$this->dataObject = $dataObject;
}
protected function getUrl() {
return "http://...";
}
protected function getData() {
return $this->dataObject->getSomeValue();
}
}
class aTest
{
public function testTheRequest() {
$dataObject = getSomeDataObject(..);
$request = new CreateSubAccountRequest($dataObject);
$request->doRequest();
$this->assertTrue($request->wasSuccessful());
}
}
?>
Note: This is a PHP5 / PHPUnit example
Given that testTheRequest is the method called by the test suite, the example will execute a live request.
Now, this live request will (hopefully, provided everything went well) do a POST request that has the side effect of altering live data.
Is this acceptable? What alternatives do I have? I can't see a way to mock out the Request object for the test. And even if I did, it would mean setting up results / entry points for every possible code path that Google's API accepts (which in this case would have to be found by trial and error), but would allow me the use of fixtures.
A further extension is when certain requests rely on certain data being Live already. Using the Google Content API as an example again, to add a Data Feed to a Sub Account, the Sub Account must already exist.
One approach I can think of is the following steps;
In testCreateAccount
Create a sub-account
Assert the sub-account was created
Delete the sub-account
Have testCreateDataFeed depend on testCreateAccount not having any errors
In testCreateDataFeed, create a new account
Create the data feed
Assert the data feed was created
Delete the data feed
Delete the sub-account
This then raises the further question; how do I test the deletion of accounts / data feeds? testCreateDataFeed feels dirty to me - What if creating the data feed fails? The test fails, therefore the sub-account is never deleted... I can't test deletion without creation, so do I write another test (testDeleteAccount) that relies on testCreateAccount before creating then deleting an account of its own (since data shouldn't be shared between tests).
In Summary
How do I test interacting with an external API that effects live data?
How can I mock / stub objects in an Integration test when they're hidden behind layers of abstraction?
What do I do when a test fails and the live data is left in an inconsistent state?
How in code do I actually go about doing all this?
Related:
How can mocking external services improve unit tests?
Writing unit tests for a REST-ful API
This is more an additional answer to the one already given:
Looking through your code, the class GoogleAPIRequest has a hard-encoded dependency of class Request. This prevents you from testing it independently from the request class, so you can't mock the request.
You need to make the request injectable, so you can change it to a mock while testing. That done, no real API HTTP requests are send, the live data is not changed and you can test much quicker.
I've recently had to update a library because the api it connects to was updated.
My knowledge isn't enough to explain in detail, but i learnt a great deal from looking at the code. https://github.com/gridiron-guru/FantasyDataAPI
You can submit a request as you would normally to the api and then save that response as a json file, you can then use that as a mock.
Have a look at the tests in this library which connects to an api using Guzzle.
It mocks responses from the api, there's a good deal of information in the docs on how the testing works it might give you an idea of how to go about it.
but basically you do a manual call to the api along with any parameters you need, and save the response as a json file.
When you write your test for the api call, send along the same parameters and get it to load in the mock rather than using the live api, you can then test the data in the mock you created contains the expected values.
My Updated version of the api in question can be found here.
Updated Repo
One of the ways to test out external APIs is as you mentioned, by creating a mock and working against that with the behavior hard coded as you have understood it.
Sometimes people refer to this type of testing as "contract based" testing, where you can write tests against the API based on the behavior you have observed and coded against, and when those tests start failing, the "contract is broken". If they are simple REST based tests using dummy data you can also provide them to the external provider to run so they can discover where/when they might be changing the API enough that it should be a new version or produce a warning about not being backwards compatible.
Ref: https://www.thoughtworks.com/radar/techniques/consumer-driven-contract-testing
A second MSpec question from me in a day, this is a new record. I'm trying to get smart on MSpec very quickly and I've run into some old problems I've always had with MSpec.
Scenario: I have a repository that contains a bunch of cartoons. Right now I only need to filter this set on a single Name parameter, which is a string. As I'm told I'll need to filter this on more properties later on, I decide to create a class which takes in my ICartoonRepository via IoC, and contains a simple method that's called GetByName(string name).
You might argue this is overkill, but I'm trying to teach myself how to use MSpec and work in a more TDD manner.
So I create the following:
[Subject(typeof(CartoonViewModelBuilder))]
public class when_cartoon_repository_is_asked_to_get_by_id : specification_for_cartoon_viewmodel_builder
{
static string name;
static Cartoon the_cartoon;
static Cartoon result;
Establish context = () =>
{
name = "Taz";
the_cartoon = new Cartoon();
the_cartoon_repository.Stub(r => r.GetAll().Where(x=>x.Name == name).FirstOrDefault()).Return(the_cartoon);
};
Because of = () => result = subject.GetByName(name);
It should_return_cartoon = () => result.ShouldBeTheSameAs(the_cartoon);
}
This fails on the stub as the repository is empty. I have a couple other tests that pass fine (simply testing the GetAll(), etc). Do I need to add things to the repository to test it? This is where I'm stumped, please be gentle.
Also, if I'm writing the linq statement in the stub, it seems like I'm doing it twice, in the actual implementation and in the test. Is this the point? It doesn't feel right. Is there a better way I can write this test?
For clarity sake, here is the actual implementation (I'm omitting the interface and the class, which just has one property:
public class CartoonViewModelBuilder: ICartoonViewModelBuilder
{
readonly ICartoonRepository _cartoonRepository;
public CartoonQueryObject(ICartoonRepository cartoonRepository)
{
_cartoonRepository = cartoonRepository;
}
public IList<Cartoon> GetAllCartoons()
{
return _cartoonRepository.GetAll();
}
public Cartoon GetByName(string name)
{
return _cartoonRepository.GetAll().Where(x => x.Name == name).FirstOrDefault();
}
}
Edit 1: Based on the lack of responses, I should say that if I were using something like NUnit, I would be creating a method on the testing class that was like, "LoadDummyData" and threw data into the repository, then I'd do complex filtering or view model building and sort of manually checked what happened. This made large refactoring a chore. It seems like specs allows you to avoid that?
Edit 2: Here's my corrected test which now passes. Let me know if I'm doing it right, I think I am. Thanks again for the hand holding!
static string name;
static Cartoon the_cartoon;
static Cartoon result;
static IQueryable<Cartoon> the_cartoons;
Establish context = () =>
{
name = "Taz";
the_cartoon = new Cartoon {Name = name};
the_cartoons = new List<Cartoon> {the_cartoon, new Cartoon(), new Cartoon() }.AsQueryable();
the_cartoon_repository.Stub(r => r.GetAll()).Return(the_cartoons.ToList());
};
Because of = () => result = subject.GetByName(name);
It should_return_cartoon = () => result.ShouldBeTheSameAs(the_cartoon);
Edit 3: Gave you both points, but I can only award one best answer unfortunately.
The actual reason of this test failing is the way you're mocking your repository. I would be very surprised if method chains like r.GetAll().Where(x=>x.Name == name).FirstOrDefault() could be mocked so easily, as it uses LINQ extension methods and lambda clauses. The framework should really throw NotSupported exception or something to let you know that you can't mock LINQ queries as a whole.
To mock LINQ query result, you should provide properly prepared underlying data collection, which is the starting point of LINQ query. In your example you should mock just r.GetAll() to return a collection containing your element with proper name. The actual query will run on your "mocked" data and retrieve the object you expect.
This removes the need to duplicate your LINQ query in code and in test, what is strange, as you noted.
EDIT: Code in your edit is like I've suggested, technically OK.
Anyway, by now it's a bit overkill, as you've said. Your class under test doesn't do anything beside the call to the mocked repository, so the value of that test is rather small. But it may be a good start if you're going to have some more logic in GetByName method.
If you want to test your repository implementation, don't stub it! MSpec or not, I would add a list of known items to the repository and then issue the query with GetByName. Then assert that just the item you expect was returned. I would also use ShouldEqual as the repository might work with the items you add and return a different instance, though considered equal (aggregate IDs are equal).