{"id":1876,"date":"2017-03-03T13:39:58","date_gmt":"2017-03-03T12:39:58","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=1876"},"modified":"2023-06-07T15:26:50","modified_gmt":"2023-06-07T13:26:50","slug":"building-an-hdm-alexa-skill-part-3","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/03\/building-an-hdm-alexa-skill-part-3\/","title":{"rendered":"Building an HdM Alexa Skill \u2013 Part 3"},"content":{"rendered":"<h2>Test-driven Development of an Alexa Skill with Node.js<\/h2>\n<p>This is the third part in a series of blog posts in which we will describe the process of developing an Amazon Alexa Skill while focusing on using new technologies like serverless computing and enforcing the use of clean code conventions. We decided for our project to use continuous integration and delivery. For that to work as it should and to prevent unnecessary bugs from being discovered by the user, we relied on test-driven development for our code.<\/p>\n<p><!--more--><\/p>\n<p><em>If you missed the first or second part you can catch up by reading them <a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/02\/16\/building-a-hdm-alexa-skill-part-1\/\">here<\/a>(1) and <a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/02\/23\/building-an-hdm-alexa-skill-part-2\/\">here<\/a>(2).<\/em><\/p>\n<p>As already presented in the introduction, our skill helps students from the HdM Stuttgart get information about classes and professors quickly by making them able to ask <strong>Amazon Alexa<\/strong>, which is a more modern and dynamic approach than searching for information on a website.<\/p>\n<p>We decided to use the Node.js platform for implementing our skill. As stated in <a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/02\/16\/building-a-hdm-alexa-skill-part-1\/\">part 1<\/a>, we wrote our own <strong>npm package<\/strong>, the <code class=\"\" data-line=\"\">hdm-client<\/code>, to encapsulate the unofficial API of our university, which helped us separate the gathering of data from the actual skill implementation. For writing both the skill and the client we used a test-driven approach. The components of our Skill looked like this:<\/p>\n<p><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1881\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/03\/building-an-hdm-alexa-skill-part-3\/components\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components.png\" data-orig-size=\"1426,246\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"hdm-alexa-skill-components\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components-1024x177.png\" class=\"aligncenter wp-image-1881 size-full\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components.png\" width=\"1426\" height=\"246\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components.png 1426w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components-300x52.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components-768x132.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/components-1024x177.png 1024w\" sizes=\"auto, (max-width: 1426px) 100vw, 1426px\" \/><\/a><\/p>\n<p>Test-Driven Development (TDD) states that you first write your test cases before even writing the first line of production code. The process of test-driven development (TDD) consists of three steps:<br \/>\n1. Write a unit test that initially fails<br \/>\n2. Write as much code as we need to make the test pass<br \/>\n3. Refactor the code until it&#8217;s simple and understandable<\/p>\n<p>We really didn&#8217;t want to write a single line of code if there was no test demanding it. That led\u00a0to a high test coverage percentage and it also forced\u00a0us to only write code that was necessary at that moment, which complies with the <em>YAGNI<\/em> design pattern (You ain&#8217;t gonna need it). We tested code coverage using a free javascript tool called <a href=\"https:\/\/istanbul.js.org\/\"><strong>Istanbul.js<\/strong><\/a>, here is a screenshot of the report as HTML and from terminal:<\/p>\n<p><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1889\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/03\/building-an-hdm-alexa-skill-part-3\/test_coverage\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage.png\" data-orig-size=\"1265,607\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"hdm-alexa-skill-code-coverage-istanbul\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage-1024x491.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage.png\" alt=\"\" width=\"1265\" height=\"607\" class=\"aligncenter size-full wp-image-1889\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage.png 1265w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage-300x144.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage-768x369.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/test_coverage-1024x491.png 1024w\" sizes=\"auto, (max-width: 1265px) 100vw, 1265px\" \/><\/a><\/p>\n<h3>Testing with Mocha and Chai<\/h3>\n<p>Luckily for us, testing Node.js applications isn&#8217;t anything new and there are plenty of good and free testing frameworks out there. We chose <a href=\"http:\/\/mochajs.org\"><strong>Mocha<\/strong><\/a> for our tests because it is one of the more robust and widely used libraries. <strong>Mocha<\/strong> doesn&#8217;t incorporate an assertion library, it works with everything that throws an exception. We used it for describing our tests and we used <a href=\"http:\/\/chaijs.com\"><strong>Chai<\/strong><\/a> to write the test&#8217;s assertions. A simple test case looked like this:<\/p>\n<pre class=\"prettyprint lang-javascript\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">describe('officeHours', function() { \/\/ Test description in Mocha\n    'use strict';\n\n    it('should be a function', function() {\n         \/\/ Self-explanatory assertions with Chai\n        expect(lecturer.officeHours).to.be.a('function');\n    });\n});<\/pre>\n<p>In our case, the skill relies heavily in making http requests, which led to a lot of asynchronous functions in our code. <strong>Mocha<\/strong> makes it easy to test asynchronous code because you can pass a <code class=\"\" data-line=\"\">done<\/code> function as a parameter and <strong>Mocha<\/strong> expects the function to be called within the callback function in a customisable period of time. The following example tests the function of the module <code class=\"\" data-line=\"\">lecturer<\/code> responsible for getting the office hours of a particular professor:<\/p>\n<pre class=\"prettyprint lang-javascript\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">describe('officeHours', function() { \/\/ Test description in Mocha\n    'use strict';\n\n    it('should throw an error if the client throws one', function(done) { \/\/ Passing in the done function\n\n        lecturer.officeHours(client \/*the hdm-client*\/, 'Professor X', function(err, response) {\n                expect(err.message).to.equal('Not Found'); \/\/ Assertions using chai.js\n                expect(response).to.equal(undefined);\n                done(); \/\/ 'done' gets called here if the callback function is called\n            });\n    });\n});<\/pre>\n<p>Here the <code class=\"\" data-line=\"\">hdm-client<\/code> gets passed into the <code class=\"\" data-line=\"\">lecturer<\/code> module following the pattern of inversion of control. This helps us decide which <em>state<\/em> of the client gets used in the <code class=\"\" data-line=\"\">officeHours<\/code> function. That&#8217;s especially helpful in this scenario because we want our unit tests to run isolated from the rest of the code. Furthermore, we also want our unit tests to run fast and for that, we need to prevent them from doing real HTTP requests to the unofficial API from our university, which in fact only parses the website looking for information. We achieved this by using <a href=\"http:\/\/sinonjs.org\"><strong>Sinon.js<\/strong><\/a>, a javascript library used for mocking, stubbing and spying javascript objects. With <strong>Sinon<\/strong> we can stub the client&#8217;s function that is used inside <code class=\"\" data-line=\"\">officeHours<\/code> to prevent it from actually being called. Instead, it returns a predefined value which we can test in our unit test code:<\/p>\n<pre class=\"prettyprint lang-javascript\" data-start-line=\"1\" data-visibility=\"visible\" data-highlight=\"\" data-caption=\"\">it('should return the office time if there's just one result', function(done) {\n    var client, stub, data, responseText;\n    \/\/ Our fake response\n    data = [{\n        name: 'Professor X',\n        officehours: 'Do 14:-16:'\n    }];\n    \/\/ Here we create a stub from the clients function searchDetails\n    stub = sinon.stub().callsArgWith(3, null, data);\n    client = { searchDetails: stub };\n    responseText = 'Professor X hat am Do 14:00-16:00 Sprechstunde.';\n\n    \/\/ Then we pass in the client object as always\n    lecturer.officeHours(client, 'Professor X', function(err, response) {\n            expect(err).to.equal(null);\n            expect(response).to.equal(responseText);\n            done();\n        });\n});<\/pre>\n<h3>The challenges we faced<\/h3>\n<p>The biggest problem for us when coding our skill test-driven was a breaking change in our dependency, the <code class=\"\" data-line=\"\">hdm-client<\/code>. We were facing problems with our code running for too long on the Amazon Lambda system, which led to timeout exceptions. We decided to change the <code class=\"\" data-line=\"\">hdm-client<\/code> to make it possible for its functions to be called with a <em>maximum results<\/em>-parameter. Of course, we knew this change would make every line of code using the client in the skill logic break since the order of the arguments in the functions changed: <em>But still none of our unit tests failed<\/em>. But how?<\/p>\n<p>The tests did not fail because we weren&#8217;t actually calling functions of the client. The <strong>Sinon<\/strong> stub prevented the real function from being called and returned our fake response immediately. We knew exactly which lines in our code were causing the problem, but simply changing them didn&#8217;t feel right. The two first steps of TDD state that you <em>first<\/em> have to write a test that fails and <em>then<\/em> write the production code you need. Long story short: we decided to introduce <strong>integration tests<\/strong>.<\/p>\n<h3>Integration tests<\/h3>\n<p>We wrote one test for each main function in our skill code, but this time we didn&#8217;t mock the client. We called this kind of tests integration tests because we were actually testing how the skill code behaves when using the real <code class=\"\" data-line=\"\">hdm-client<\/code>. We watched all tests fail and then changed the code in the skill&#8217;s logic to make them pass. Then all of our unit tests failed so we had to fix those as well.<\/p>\n<p><em>The unit tests failed because of the client&#8217;s stub calling the callback function with the wrong order of arguments.<\/em><\/p>\n<p>Our integration tests did take longer to complete than regular unit tests (around 200 to 400ms each) because of their data, which actually does the whole roundtrip to the unofficial HdM API. With this procedure, we weren&#8217;t able to test if the data coming back was valid because we depended on many networking factors, but the purpose of the tests was finding out if the skill logic uses the <code class=\"\" data-line=\"\">hdm-client<\/code> correctly. We also didn&#8217;t need to run them as often as unit tests, so we usually just ran them in our CI-Server.<\/p>\n<h3>Lessons learned<\/h3>\n<p>Breaking changes to a dependency can be a pain in the neck, whether you are programming test-driven or not. If you are using a TDD approach, fixes to the code base should always be led by an appropriate test that also fails at first. Unit tests do not always warn you about existing problems. This time we knew breaking changes were coming with an update to our dependency because we developed that library, but that is only rarely the case. You should always do <em>end-to-end<\/em> testing before publishing or pushing your changes to the production environment. In our case, it&#8217;s difficult to automate end-to-end tests because we need to talk to Alexa to test our skill and we also need to hear the answer, but for that, we wrote down our test cases and tested them one by one talking to our Echo Dot. Without knowledge of the dependency changes and without any integration tests, that would have been the only way of finding out there was a problem.<\/p>\n<p>Even though making quick fixes might seem to take longer with <strong>TDD<\/strong>, we were glad to use this approach, since our failing tests were perfect for finding every line in our codebase which was still making a wrong call to the client. Without a test for each line of production code, we might still have parts of our codebase we forgot to change, only waiting for a user to do an unusual request to our service, making the skill logic crash.<\/p>\n<p><em>Please don&#8217;t hesitate to leave us a comment and tell us what your experience with Test-Driven Development was.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We present our own HdM Alexa Skill and share the experience we gained throughout this project. This time: Developing the skill using Test-driven Development.<\/p>\n","protected":false},"author":190,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[22,651,2],"tags":[76,77,25,23],"ppma_author":[720],"class_list":["post-1876","post","type-post","status-publish","format-standard","hentry","category-student-projects","category-system-designs","category-system-engineering","tag-amazon-alexa-skill","tag-amazon-web-services","tag-nodejs","tag-tdd"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1807,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/02\/16\/building-a-hdm-alexa-skill-part-1\/","url_meta":{"origin":1876,"position":0},"title":"Building a HdM Alexa Skill &#8211; Part 1","author":"Eric Schmidt","date":"16. February 2017","format":false,"excerpt":"We present our own HdM Alexa Skill and share the experience we gained throughout this project.","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/chatbot.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/chatbot.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/chatbot.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/chatbot.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/chatbot.jpg?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":1863,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/02\/23\/building-an-hdm-alexa-skill-part-2\/","url_meta":{"origin":1876,"position":1},"title":"Building an HdM Alexa Skill &#8211; Part 2","author":"Andreas Fliehr","date":"23. February 2017","format":false,"excerpt":"We present our own HdM Alexa Skill and share the experience we gained throughout this project. This time: Decisions, Developed Modules and Implementation.","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/data_flow.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/data_flow.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/data_flow.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/data_flow.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/data_flow.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":2633,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/08\/28\/how-to-build-an-alexa-skill-to-get-information-about-your-timetable\/","url_meta":{"origin":1876,"position":2},"title":"How to build an Alexa Skill to get information about your timetable","author":"dm080@hdm-stuttgart.de","date":"28. August 2017","format":false,"excerpt":"Introduction With information technology today we can easily get any kind of information someone is interested in. Whether you want to know how the weather will be tomorrow or how to cook your favorite cake, you can find out almost anything today. But as a user it\u2019s getting more important\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/08\/RequestManager_UML.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":4213,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/09\/02\/how-to-build-an-alexa-skill-to-get-information-about-your-timetable-2018-version\/","url_meta":{"origin":1876,"position":3},"title":"How to build an Alexa Skill to get information about your timetable 2018 Version","author":"nk068","date":"2. September 2018","format":false,"excerpt":"Imagine a student who just got up. He knows exactly that he has lectures today, but he does not remember which one or even when it begins. So, he asks his Alexa device: \u201cAlexa, which classes do I have today?\u2019\u201d His Alexa device is able to look into his timetable\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/09\/Figure6.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/09\/Figure6.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/09\/Figure6.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/09\/Figure6.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1984,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2017\/03\/09\/building-an-hdm-alexa-skill-part-4\/","url_meta":{"origin":1876,"position":4},"title":"Building an HdM Alexa Skill &#8211; Part 4","author":"Malte Vollmerhausen","date":"9. March 2017","format":false,"excerpt":"We present our own HdM Alexa Skill and share the experience we gained throughout this project. This time: Automating tests and deployment with Continuous Integration via Jenkins.","rel":"","context":"In &quot;Student Projects&quot;","block_context":{"text":"Student Projects","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/student-projects\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2017\/02\/jenkins.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":556,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2016\/05\/27\/test-driven-development-with-node-js\/","url_meta":{"origin":1876,"position":5},"title":"Test Driven Development with Node.js","author":"Roman Kollatschny","date":"27. May 2016","format":false,"excerpt":"Test-Driven Development with Mocha and Chai in Node.js","rel":"","context":"In &quot;Rich Media Systems&quot;","block_context":{"text":"Rich Media Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/interactive-media\/rich-media-systems\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":720,"user_id":190,"is_guest":0,"slug":"je052","display_name":"je052","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/576fb464cfe916419a60601453c38b7bf423d3d899a448f675e0f54507f4d63a?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/1876","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/190"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=1876"}],"version-history":[{"count":26,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/1876\/revisions"}],"predecessor-version":[{"id":2249,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/1876\/revisions\/2249"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=1876"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=1876"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=1876"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=1876"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}