Intro
If you haven’t written tests for GTM templates yet, please read the great Simo’s post about what Test API is and how to use it. Also please have a look at the Test API documentation. In this post I assume that you already play with tests, and I will just share some cool and practical (I hope) examples. Let’s go and have some test fun.
Get the data fast
If you build a Template with a complex table or a Template with a huge list of dependent fields - you can save your time if you add a few lines in your code:
|
|
And after that in the Template Preview section fill the fields with dummy values and click the Run Code button. Here’s an example for my Firestore Web Tag:
That’s all, now you can copy the value from the console and use it for your tests, slightly changing it for different cases as needed.
Use the Setup
Don’t ignore the Setup section of the Test Tab interface. The good idea is to set the default values for all your tests inside Setup to keep your tests DRY. These values can be:
let mockData = {...}
with the most common state. And in each test you can change only one or two properties needed to be changed and pass the value torunCode
method. For example like this:
|
|
- Add all libraries you will use in tests. That’s right you can require some libraries inside tests (but not all). For example:
|
|
Define functions you will use in a few tests, for example functions which mock
Promise
,Firestore
orCookie
logic, we will cover these API later in the post.If you use
getTimestampMillis
inside your code and later check it value or compare it with the input values don’t forget to mock the time:
|
|
- For Server-Side Tags it’s a good idea to set a default event state like this:
|
|
And later in a test you can change some property and mock getAllEventData API
|
|
- For a Server-Side Client tests don’t forget to mock claimRequest like this:
|
|
Otherwise, if you try Run All
tests only the first one will work and the others will return errors:
fail
Short trick, if for a test case you want to be sure that some method wouldn’t be fired you can mock it with a fail inside, like this:
|
|
In this example If I have a bug in the logic and setResponseStatus will be fired the test fails.
assertThat
assertThat is a very handy little API we will use it practically everywhere inside mocks, if we want to check parameters passed into a function.
|
|
You will see a lot of examples later in the post.
API examples
Ok and now let’s go to the API examples, for each case I’ll show the code and the test example, we will start from the easiest API and leave the Firestore API for the last.
DataLayer push
Code
|
|
Test
|
|
Comment:
It is useful when you expect that at the end a Template will make a dataLayer
push with a result state. This way we can check the result is correct.
Cookies
Code
|
|
Test
|
|
Comment
In this example we check cookieName, and for different names assert different values and raise failure for unexpected cookies. It’s also a good idea to add all possible variants and move this mock to the Setup section.
SHA256
Code
|
|
Test
|
|
Comment
This is an example of callback testing. The code hashes some value and passes it as a parameter to sendPixel API. Again, sha256 API after hashing will execute a callback and pass a hashed value to this callback.
So if we want to test this case, we can mock sha256
with a function which runs a callback straight away and passes a hashed value in this callback. For a hashing algorithm we can use very simple logic - just add _hashed
at the end of the string.
And as a second step we mock sendPixel and expect it will be fired with the URL with test_hashed
at the end, because unhashed value was test
and after our mock hashing it becomes test_hashed
Consent
Code
|
|
Test
|
|
Comment
Here in the code we check ad_storage
and if it hasn’t been granted we add the listener. In our test we want to check a case – at first the ad_storage
isn’t granted, but it will be granted later.
For this case, in the test we mock isConsentGranted to return ad_storage
equal false, and mock addConsentListener to return granted. But the funny thing here is how we work with callbacks. Please notice in the code, we pass the callback function to addConsentListener
:
|
|
This callback function has two parameters: the consentType and the consent status.
In the test we mock addConsentListener
with a function which immediately calls callback and pass values we
expected “ad_storage” and “granted” like this:
|
|
localStorage
Code
|
|
Test
|
|
Comment
This one was really interesting for me. Unfortunately you can’t actually mock localStorage API, so we need a little trick here.
In the test we mock localStorage
with a function which returns an object with getItem
property, and this property itself is a function which returns our expected value.
In the code we check a type of the localStorage
and if it’s a function it means we are inside a test, and initialize ls
with the result of the localStorage
function, otherwise we are not in the test and use localStorage API
as always.
|
|
Later in the code we always use ls
instead of localStorage
, this way in tests we can inject localStorage
values we need.
Promise API
For Promise API I created a separate blog post as this API is very handy and beautiful, and deserves a bit more time to play with. Please have a look at the link provided.
One important note about Promise and callbacks – unfortunately assertApi doesn’t work for API methods inside promises, you have to use assertThat inside mock if you want to check a method was called.
Firestore API
Code
|
|
Test
|
|
Comment
For Firestore API I use tricks from localStorage
and Promise API
so please read them first. The important note, if your code uses Firestore API
– you have to mock it, or your tests will always try to make requests to Firestore, and you get access errors or even worse, can corrupt your data, please be very careful.
In this example I show the most interesting case when you use transactions.
In the code at first we do the same trick as for Promise API
, it helps us to inject mocked Firestore
.
|
|
After that I use the sample code from the documentation, it gets value from Firestore and increases inputCount
by one. In the code we use Firestore.runTransaction, Firestore.read, Firestore.write - and we need to mock all these methods.
In the test I create a mockExistingUser
function, you can move it to the Setup sections and later call in all test cases. This function mock all three Firestore methods, for read method it returns userStateBeforeFormatted
object, for write methods it compares input value with userWriteState
object. In our test case the read method returns inputCount
equal 10 and the write method expects inputCount
should be 11.
If you need to test a scenario in which a Template requests not existing value from the Firestore, in this case a mock for the read method should return a promise which call a reject
, something like this:
|
|
You can find more Firestore test examples in my Firestore Server-Side Tag. If you want to learn more about this Template please read the post on how to get / set user state using Firestore and GTM.
That’s all for now. I hope this post will help you to test GTM Templates, happy testing!
If you have any questions or ideas, please message me on LinkedIn.