<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Spring Boot de Hello World a API lista para producción on juanfbermejo.dev</title><link>/series/spring-boot-de-hello-world-a-api-lista-para-produccion/</link><description>Recent content in Spring Boot de Hello World a API lista para producción on juanfbermejo.dev</description><image><title>juanfbermejo.dev</title><url>/favicon-32x32.png</url><link>/favicon-32x32.png</link></image><generator>Hugo -- 0.152.2</generator><language>es</language><lastBuildDate>Sat, 04 Apr 2026 17:24:05 +0200</lastBuildDate><atom:link href="/series/spring-boot-de-hello-world-a-api-lista-para-produccion/index.xml" rel="self" type="application/rss+xml"/><item><title>Sustituyendo Map por un DTO en nuestra API</title><link>/posts/sustituyendo-map-dto-api/</link><pubDate>Sat, 04 Apr 2026 17:24:05 +0200</pubDate><guid>/posts/sustituyendo-map-dto-api/</guid><description>&lt;p&gt;Si nuestros endpoints devuelven &lt;code&gt;String&lt;/code&gt; o &lt;code&gt;Map&lt;/code&gt;, ya contamos con una API plenamente funcional, pero no es la mejor solución. El problema es que &lt;strong&gt;la estructura de la respuesta está demasiado abierta&lt;/strong&gt;, lo que la hace más &lt;strong&gt;frágil y difícil de mantener&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Para especificar de forma clara el contrato de la API, es conveniente introducir en este punto los DTO (&lt;em&gt;Data Transfer Objects&lt;/em&gt;).&lt;/p&gt;
&lt;h1 id="el-problema-de-devolver-un-map"&gt;El problema de devolver un Map&lt;/h1&gt;
&lt;p&gt;Hasta ahora, nuestra API devuelve datos, pero sin estar representados de forma explícita y mantenible. Spring serializa el &lt;code&gt;Map&lt;/code&gt; sin problema a JSON, pero presenta una serie de inconvenientes que lo hacen poco adecuado para una API que empieza a evolucionar:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Si nuestros endpoints devuelven <code>String</code> o <code>Map</code>, ya contamos con una API plenamente funcional, pero no es la mejor solución. El problema es que <strong>la estructura de la respuesta está demasiado abierta</strong>, lo que la hace más <strong>frágil y difícil de mantener</strong>.</p>
<p>Para especificar de forma clara el contrato de la API, es conveniente introducir en este punto los DTO (<em>Data Transfer Objects</em>).</p>
<h1 id="el-problema-de-devolver-un-map">El problema de devolver un Map</h1>
<p>Hasta ahora, nuestra API devuelve datos, pero sin estar representados de forma explícita y mantenible. Spring serializa el <code>Map</code> sin problema a JSON, pero presenta una serie de inconvenientes que lo hacen poco adecuado para una API que empieza a evolucionar:</p>
<ol>
<li><strong>No está tipado</strong>: No existe una estructura definida que indique qué campos debe contener la respuesta.</li>
<li><strong>Sin documentación clara</strong>: la forma del JSON no queda reflejada en ningún tipo o clase, dificultando entender qué devuelve realmente el endpoint.</li>
<li><strong>Fácil equivocarse en claves</strong>: las claves son cadenas de texto, por lo que un error tipográfico puede pasar desapercibido hasta tiempo de ejecución.</li>
<li><strong>Un cambio de nombre rompe contratos con clientes fácilmente</strong>: modificar una clave puede afectar a clientes que dependen de esa estructura sin que el compilador detecte el problema.</li>
<li><strong>Perdemos claridad y el apoyo del compilador durante el desarrollo</strong>: no contamos con autocompletado ni comprobaciones de tipos al trabajar con los datos.</li>
<li><strong>No es escalable</strong>: a medida que la respuesta crece, el <code>Map</code> se vuelve más difícil de leer, mantener y evolucionar.</li>
</ol>
<p>El problema no es que falle, <strong>es que la estructura de la respuesta queda implícita, dispersa y demasiado abierta</strong>.</p>
<h1 id="qué-es-un-dto">Qué es un DTO</h1>
<p>Un DTO (<em>Data Transfer Object</em>) <strong>es un objeto cuyo objetivo es transportar datos entre capas o entre sistemas</strong>. En una API, uno de sus usos más habituales es representar de forma explícita los datos que enviamos o recibimos en los diferentes endpoints.</p>
<p>Son objetos simples cuya función es <strong>representar datos con una estructura clara y fija</strong>, sin contener ninguna lógica de negocio. De esta forma, la estructura de la respuesta queda definida de manera explícita y controlada.</p>
<h1 id="primer-dto-en-nuestra-api">Primer DTO en nuestra API</h1>
<p>La respuesta que devolvemos actualmente ya está serializada en un JSON con el siguiente contenido:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Juan&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-04-04T11:30:38.578535500Z&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>Spring la está formando a partir de un Map en el endpoint:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;timestamp&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Un primer DTO para la respuesta de nuestro endpoint podría ser el siguiente, incluyendo un atributo por cada campo que queremos devolver:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloResponseDto</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">HelloResponseDto</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">message</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">timestamp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getMessage</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getTimestamp</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Quedando el endpoint de la siguiente manera, sin alterar la respuesta de la petición:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">HelloResponseDto</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">HelloResponseDto</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HelloResponseDto</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Ahora la estructura de la respuesta queda definida de forma explícita en un tipo Java, facilitando su comprensión y evolución.</p>
<h1 id="usando-record-de-java">Usando <code>record</code> de Java</h1>
<p>Como el objeto <code>HelloResponseDto</code> solo transporta datos, Java ofrece una forma más compacta de representarlo: <code>record</code>. Un <code>record</code> <strong>define datos inmutables, reduce el código repetitivo y encaja muy bien en objetos simples de transporte</strong>.</p>
<p>Podríamos redefinir la clase <code>HelloResponseDto</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">record</span> <span class="nc">HelloResponseDto</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  </span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>El endpoint no necesita modificarse, ya que el <code>record</code> implementa automáticamente el constructor y los métodos de acceso.</p>
<h1 id="ventajas-frente-a-map">Ventajas frente a Map</h1>
<ol>
<li>Un <code>Map</code> describe datos de forma improvisada; un DTO describe datos de forma intencional.</li>
<li>Más claridad: el nombre <code>HelloResponseDto</code> expresa qué estamos devolviendo.</li>
<li>Estructura explícita: los campos están definidos en un tipo.</li>
<li>Menos errores involuntarios en el desarrollo: no dependemos de escribir claves String a mano</li>
<li>Mejor mantenimiento: el cambio está centralizado.</li>
<li>Mejor evolución: más fácil añadir nuevos campos sin desordenar el controlador</li>
</ol>
<h1 id="evolución-de-nuestra-api">Evolución de nuestra API</h1>
<p>En el endpoint <code>/hello</code> comenzamos devolviendo un <code>String</code>, luego mejoramos la respuesta con <code>ResponseEntity</code>. Después pasamos a devolver un JSON. Y ahora damos un paso más definiendo explícitamente su estructura mediante DTOs. De esta forma, el contrato con los clientes se vuelve más robusto, mantenible y fiable, sentando además una base sólida para seguir evolucionando la API.</p>
]]></content:encoded></item><item><title>Mejorando la respuesta de nuestra API</title><link>/posts/mejorando-respuesta-api/</link><pubDate>Mon, 30 Mar 2026 17:11:14 +0200</pubDate><guid>/posts/mejorando-respuesta-api/</guid><description>&lt;p&gt;Hasta ahora nuestro código está devolviendo un &lt;code&gt;String&lt;/code&gt; de forma directa que &lt;strong&gt;Spring Boot convierte automáticamente en una respuesta HTTP completa&lt;/strong&gt;. Ésta, por defecto, incluye un código de estado (&lt;code&gt;200 OK&lt;/code&gt;) y un cuerpo con el mensaje.&lt;/p&gt;
&lt;p&gt;El siguiente paso para mejorar el diseño de nuestra API es &lt;strong&gt;controlar completamente la respuesta y adaptarla a nuestra lógica&lt;/strong&gt;, para lo que contamos con la clase &lt;code&gt;ResponseEntity&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="qué-es-una-respuesta-http"&gt;Qué es una respuesta HTTP&lt;/h1&gt;
&lt;p&gt;Una &lt;strong&gt;respuesta HTTP&lt;/strong&gt; es el mensaje que devuelve el servidor cuando un cliente realiza una petición a una API. Esta respuesta incluye principalmente &lt;strong&gt;un código de estado&lt;/strong&gt;, que indica si la operación ha sido correcta o ha ocurrido algún problema, y &lt;strong&gt;un cuerpo con los datos&lt;/strong&gt; que queremos devolver.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hasta ahora nuestro código está devolviendo un <code>String</code> de forma directa que <strong>Spring Boot convierte automáticamente en una respuesta HTTP completa</strong>. Ésta, por defecto, incluye un código de estado (<code>200 OK</code>) y un cuerpo con el mensaje.</p>
<p>El siguiente paso para mejorar el diseño de nuestra API es <strong>controlar completamente la respuesta y adaptarla a nuestra lógica</strong>, para lo que contamos con la clase <code>ResponseEntity</code>.</p>
<h1 id="qué-es-una-respuesta-http">Qué es una respuesta HTTP</h1>
<p>Una <strong>respuesta HTTP</strong> es el mensaje que devuelve el servidor cuando un cliente realiza una petición a una API. Esta respuesta incluye principalmente <strong>un código de estado</strong>, que indica si la operación ha sido correcta o ha ocurrido algún problema, y <strong>un cuerpo con los datos</strong> que queremos devolver.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">200 OK
</span></span><span class="line"><span class="cl">Content-Type: application/json
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ &#34;Hello World from Spring Boot!&#34; }</span></span></code></pre></div>
<p>En nuestro caso:</p>
<ul>
<li><strong>200 OK</strong>: la petición se ha procesado correctamente.</li>
<li><strong>Content-Type</strong>: indica el tipo de contenido, JSON en este caso.</li>
<li><strong>Hello World from Spring Boot!</strong>: cuerpo de la respuesta, un <code>String</code> que Spring Boot serializa automáticamente.</li>
</ul>
<p>Este concepto es importante porque, a medida que nuestra API evolucione, no solo devolveremos texto, sino también <strong>distintos códigos HTTP y estructuras más completas</strong>, y ahí es donde entra <code>ResponseEntity</code>.</p>
<h1 id="controlando-la-respuesta-con-responseentity">Controlando la respuesta con ResponseEntity</h1>
<p><code>ResponseEntity</code> es una clase de Spring que permite <strong>controlar completamente la respuesta HTTP</strong> que devuelve nuestra API. En lugar de devolver solo el contenido, podemos definir también <strong>el código de estado</strong>, las <strong>cabeceras</strong> y el <strong>cuerpo de la respuesta</strong>.</p>
<p>Hasta ahora, nuestro controlador devuelve un <code>String</code> directamente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Podemos usar <code>ResponseEntity</code> para indicar explícitamente el código HTTP que debe incluir:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">String</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En este caso seguimos devolviendo el mismo contenido, pero ahora <strong>estamos construyendo explícitamente una respuesta HTTP</strong>, algo fundamental cuando queramos devolver errores (códigos <code>400</code> o <code>404</code>), recursos creados ( código <code>201</code>) o respuestas más complejas.</p>
<h1 id="completando-la-respuesta-con-un-json">Completando la respuesta con un JSON</h1>
<p>Otra mejora para conseguir una respuesta más adecuada para una API es <strong>devolver el contenido como un objeto JSON bien estructurado</strong>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En el caso de un <code>String</code> simple puede parecer de poco interés, pero lo habitual será que <strong>la respuesta incluya mucha más información</strong>, de forma que <strong>un JSON permita estructurar mejor los datos</strong>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;timestamp&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si levantamos el servidor y, como venimos haciendo, realizamos una petición desde el navegador, obtendremos la respuesta con el JSON completo.</p>
<p><img alt="Pantalla del navegador con la respuesta JSON de la API" loading="lazy" src="/posts/mejorando-respuesta-api/images/json_response.png"></p>
<h1 id="validando-la-api-con-los-test">Validando la API con los test</h1>
<p>Una vez más, <strong>debemos usar los test para validar los cambios realizados</strong>. Hasta ahora, el contrato de nuestra API incluía una respuesta HTTP formada automáticamente con un único <code>String</code> en el cuerpo. Pero ahora, <strong>la respuesta es mucho más completa, con otro formato y con el mensaje dentro de un JSON</strong>.</p>
<p>Si ejecutamos los test existentes, éstos fallarán, <strong>indicando que hemos roto ese contrato</strong>.</p>
<h2 id="cambio-de-contrato-con-los-clientes">Cambio de contrato con los clientes</h2>
<p>Si nuestra API se encontrase en producción, esto nos indicaría que tendríamos un problema que podríamos afrontar con dos planteamientos:</p>
<ul>
<li>Obligar a todos nuestros clientes a actualizar sus desarrollos para adaptarse al nuevo formato de respuesta.</li>
<li>Versionar nuestra API, manteniendo el endpoint actual sin modificar y publicar el nuevo en una nueva ruta, permitiendo a los clientes migrar de forma progresiva.</li>
</ul>
<h2 id="evolucionando-los-tests">Evolucionando los tests</h2>
<p>Para validar el nuevo desarrollo, adaptamos los test a la nueva respuesta del endpoint.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithoutParamReturnsDefaultValue</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.message&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.name&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.timestamp&#34;</span><span class="p">).</span><span class="na">isString</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithParamReturnsCustomName</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="na">param</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">())</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.message&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.name&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.timestamp&#34;</span><span class="p">).</span><span class="na">isString</span><span class="p">());</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><code>jsonPath(&quot;$.message&quot;)</code> indica a <code>MockMvc</code> que acceda al campo <code>message</code> del JSON devuelto:</p>
<ul>
<li><code>$</code>: representa la raíz del JSON</li>
<li><code>$.message</code>: accede al campo <code>message</code></li>
<li><code>.value(...)</code>: comprueba que ese campo tiene el valor esperado</li>
</ul>
<p>Por lo tanto, permite <strong>validar un campo concreto dentro del JSON que devuelve la API</strong> y verifica que:</p>
<ul>
<li>La respuesta tiene formato JSON</li>
<li>Existe el campo <code>message</code></li>
<li>Su valor es exactamente el esperado en cada caso</li>
</ul>
<p>Este enfoque permite <strong>validar respuestas más complejas de forma precisa</strong>, comprobando cada campo individualmente en lugar de comparar todo el contenido como una cadena de texto, lo que hace los tests más robustos y fáciles de mantener.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Mejorando la respuesta de nuestra API, pasando de devolver texto simple a diseñar respuestas HTTP más completas, estamos evolucionándola de forma natural para conseguir un resultado más profesional. Además, con el trabajo continuo con los test, conseguimos validar el contrato que establecemos con nuestros clientes, de forma que podamos realizar esa evolución con seguridad.</p>
]]></content:encoded></item><item><title>Recibiendo datos en una API con @RequestParam</title><link>/posts/recibiendo-datos-request-param/</link><pubDate>Sat, 21 Mar 2026 10:21:14 +0100</pubDate><guid>/posts/recibiendo-datos-request-param/</guid><description>&lt;p&gt;Una API es &lt;strong&gt;la puerta de entrada a nuestra aplicación&lt;/strong&gt;: recibe peticiones desde el exterior,
extrae los datos que le envían, ejecuta la lógica necesaria y devuelve una respuesta. Hasta
ahora, en &lt;a href="/posts/spring-boot-hello-world/"&gt;nuestra API &lt;em&gt;HelloWorld&lt;/em&gt;&lt;/a&gt; solo recibimos
peticiones y damos una respuesta. El siguiente paso es &lt;strong&gt;permitir que un cliente nos pase
información a través de la URL&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="formas-en-las-que-una-api-puede-recibir-datos"&gt;Formas en las que una API puede recibir datos&lt;/h1&gt;
&lt;p&gt;El componente encargado de extraer los datos de la petición es el controlador. En una API HTTP, éstos pueden llegar al endpoint de varias maneras según su finalidad:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una API es <strong>la puerta de entrada a nuestra aplicación</strong>: recibe peticiones desde el exterior,
extrae los datos que le envían, ejecuta la lógica necesaria y devuelve una respuesta. Hasta
ahora, en <a href="/posts/spring-boot-hello-world/">nuestra API <em>HelloWorld</em></a> solo recibimos
peticiones y damos una respuesta. El siguiente paso es <strong>permitir que un cliente nos pase
información a través de la URL</strong>.</p>
<h1 id="formas-en-las-que-una-api-puede-recibir-datos">Formas en las que una API puede recibir datos</h1>
<p>El componente encargado de extraer los datos de la petición es el controlador. En una API HTTP, éstos pueden llegar al endpoint de varias maneras según su finalidad:</p>
<ol>
<li>En la URL como parámetros</li>
<li>En la URL como parte de la ruta</li>
<li>En el cuerpo de la petición</li>
<li>En las cabeceras de la petición</li>
<li>En cookies</li>
<li>Como formularios</li>
<li>Como ficheros</li>
</ol>
<p>Vamos a ver la primera y más sencilla de ellas: recibir datos como parámetros de la URL.</p>
<h1 id="primer-dato-de-entrada-en-nuestra-api">Primer dato de entrada en nuestra API</h1>
<p>Nuestra API de momento, está devolviendo un saludo genérico. A partir de aquí, <strong>empezamos a
construir APIs que reaccionan a lo que les envía el cliente</strong>.</p>
<pre tabindex="0"><code class="language-URL" data-lang="URL">http://localhost:8080/hello?name=Juan</code></pre>
<p>Para recibirlo en el controlador y poder utilizarlo, incorporamos un parámetro con el mismo
nombre al método del endpoint y lo anotamos con <code>@RequestParam</code>. De esta forma Spring Boot extrae
automáticamente el valor de la URL y lo pasa como argumento al método.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si desplegamos la aplicación y probamos desde el navegador, vemos el resultado:</p>
<p><img alt="Navegador web con la respuesta de la API al pasar parámetro en la URL" loading="lazy" src="/posts/recibiendo-datos-request-param/images/HelloJuan.png"></p>
<p>Pero, ¿y si no incluimos el parámetro <code>name</code> en la URL?</p>
<h2 id="parámetros-opcionales">Parámetros opcionales</h2>
<p>Con la configuración actual, la aplicación está esperando siempre recibir el parámetro <code>name</code> en la petición, devolviendo un error de no ser así.</p>
<p>Para evitarlo, <strong>podemos marcar los parámetros como opcionales</strong> con la propiedad <code>required</code> de la
siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">required</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En este caso, si no se envía el parámetro, Spring asigna el valor <code>null</code>.</p>
<p><img alt="Navegador web con la respuesta de la API por defecto al no recibir parámetro en la URL" loading="lazy" src="/posts/recibiendo-datos-request-param/images/HelloWorld.png"></p>
<h2 id="valores-por-defecto">Valores por defecto</h2>
<p>La anotación cuenta con la propiedad <code>defaultValue</code> que nos permite simplificar la lógica en el
controlador, <strong>fijando un valor por defecto si no se informa el parámetro</strong>, en cuyo caso Spring asume automáticamente que se trata de un parámetro opcional. Cuando usamos <code>defaultValue</code>,
<strong>Spring considera automáticamente que el parámetro es opcional</strong>, por lo que no es necesario
indicar <code>required = false</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="cuando-usar-parámetros-en-la-url">Cuando usar parámetros en la URL</h1>
<p>Los <code>@RequestParam</code> se utilizan cuando queremos recibir datos simples en la URL, normalmente para:</p>
<ul>
<li>filtros</li>
<li>búsquedas</li>
<li>valores opcionales</li>
</ul>
<h1 id="los-tests-como-red-de-seguridad-ante-cambios">Los tests como red de seguridad ante cambios</h1>
<p>Cada vez que cambiamos el comportamiento de un endpoint, <strong>los tests nos ayuda a comprobar si de
cara al cliente el cambio ha sido transparente</strong>. No solo validan el código, también documentan
cómo debe comportarse la API.</p>
<p>Si lanzamos <a href="/posts/primer-test-spring-boot/">los tests que ya habíamos implementado</a>, vemos
que siguen ejecutándose correctamente. Esto ocurre porque hemos mantenido el comportamiento por
defecto gracias al uso de <code>defaultValue</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloEndpointReturnsHelloWorld</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Consola donde se comprueba que los tests han finalizado correctamente" loading="lazy" src="/posts/recibiendo-datos-request-param/images/TestsSuccess.png"></p>
<h2 id="los-tests-evolucionan-con-el-código">Los tests evolucionan con el código</h2>
<p>Unos test útiles y completos deben <strong>cubrir los distintos escenarios posibles</strong>, y deben
<strong>ampliarse junto con el código de la API</strong>. Nuestro test cubre el comportamiento por defecto,
pero deberemos completarlo ahora para el caso de recibir un parámetro.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithParamReturnsCustomName</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="na">param</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">())</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">));</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="elementos-nuevos-que-usa-el-test">Elementos nuevos que usa el test</h3>
<h4 id="herramientas-para-simular-peticiones-http">Herramientas para simular peticiones HTTP</h4>
<ul>
<li><code>param()</code>: Spring Boot añade este parámetro a la URL como si el cliente lo hubiera enviado en la petición real.</li>
</ul>
<h3 id="nombres-que-reflejen-intención">Nombres que reflejen intención</h3>
<p>Adicionalmente, para reflejar mejor qué caso valida cada test, actualizamos también el nombre
del test previo:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithoutParamReturnsDefaultValue</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Lanzamos de nuevo la ejecución de los tests y comprobamos que finalizan correctamente.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Las APIs son las puertas de entrada a nuestras aplicaciones por lo que será habitual recibir
datos en alguno de los formatos y por alguna de las vías que proporciona Spring Boot. Los
parámetros en la URL son una de las más sencillas y útiles. A partir de aquí, iremos incorporando
nuevas formas de recibir datos en nuestra API a medida que evolucionamos sus funcionalidades.</p>
<p>También, cada vez que cambiamos el comportamiento de nuestra API, los tests nos ayudan a validar
y revisar los cambios de forma que podamos detectar si estamos modificando algún caso de uso que no teníamos contemplado, pero para que sigan siendo útiles, deben completarse a medida que ampliamos nuestra API.</p>
]]></content:encoded></item><item><title>Primer test en Spring Boot</title><link>/posts/primer-test-spring-boot/</link><pubDate>Fri, 06 Feb 2026 16:23:02 +0100</pubDate><guid>/posts/primer-test-spring-boot/</guid><description>&lt;p&gt;Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: &lt;strong&gt;el análisis, la documentación… los tests y las pruebas&lt;/strong&gt;. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE y empezar a &lt;em&gt;picar código&lt;/em&gt;, cuando muchas veces &lt;strong&gt;lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: <strong>el análisis, la documentación… los tests y las pruebas</strong>. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE  y empezar a <em>picar código</em>, cuando muchas veces <strong>lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente</strong>.</p>
<p>Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.</p>
<p>Sin embargo, al igual que un buen análisis, diseño y planificación nos ahorra tiempo y evita errores a largo plazo, <strong>un buen diseño de los casos de prueba y su automatización contribuye a obtener un mejor resultado final</strong>. Además, nos garantiza que podremos detectar rápidamente si un nuevo evolutivo introduce errores que no habíamos contemplado.</p>
<h1 id="testing-en-spring-boot">Testing en Spring Boot</h1>
<p>Spring Boot incorpora <em>de serie</em> varias herramientas que facilitan enormemente la implementación de los primeros tests. Entre ellas destacan:</p>
<ul>
<li><strong>JUnit 5</strong>: es un framework de testing que nos permite definir y ejecutar test en Java mediante anotaciones como <code>@Test</code>.</li>
<li><strong>Spring Boot Test</strong>: conjunto de utilidades que facilita probar aplicaciones Spring cargando el contexto, inyectando dependencias y simulando peticiones HTTP.</li>
<li><strong>Maven configurado para ejecutar test</strong>: Maven ejecuta automáticamente los test durante el ciclo de compilación, fallando el proceso si alguno no pasa y evitando la generación de artefactos incorrectos.</li>
</ul>
<h1 id="primer-test-de-hello-world">Primer test de Hello World</h1>
<p>Para implementar los primeros tests nos vamos a basar en el <a href="/posts/spring-boot-hello-world/"><em>Hello World</em> que desarrollamos en el artículo anterior</a> y que <a href="/posts/compilar-app-maven/">ya hemos compilado con Maven</a>.</p>
<p>Al crear la estructura básica del proyecto con Spring Initializr e incluir la dependencia <em>Spring Web</em>, ya deberíamos tener en el <code>pom.xml</code> la dependencia <code>spring-boot-starter-webmvc-test</code>, que nos proporciona todo lo necesario para estos primeros tests.</p>
<h2 id="la-aplicación-arranca">La aplicación arranca</h2>
<p>Se trata de un test de contexto, anotado con <code>@SpringBootTest</code>, que ya tendremos creado automáticamente en la estructura del proyecto dentro de la carpeta <code>src/test</code>. En nuestro caso se denomina <code>SpringBootHelloWorldApplicationTests</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">class</span> <span class="nc">SpringBootHelloWorldApplicationTests</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">contextLoads</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Este test no contiene lógica alguna, pero precisamente ahí reside su valor. Comprueba que el contexto de Spring arranca correctamente y, si falla, indica que existe un problema grave en la configuración de la aplicación.</p>
<h2 id="testear-un-endpoint-sencillo-hello-world">Testear un endpoint sencillo: Hello World</h2>
<p>El siguiente paso es diseñar e implementar un test para nuestro endpoint <code>/hello</code>. Aunque se trata de un endpoint muy sencillo, ya podemos validar tres aspectos básicos:</p>
<ol>
<li>Que el endpoint existe bajo la URL</li>
<li>Que responde con código HTTP 200</li>
<li>Que devuelve el contenido esperado</li>
</ol>
<p>El código del test es el siguiente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.MockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.content</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.status</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@AutoConfigureMockMvc</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloControllerTest</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">MockMvc</span><span class="w"> </span><span class="n">mockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">helloEndpointReturnsHelloWorld</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="elementos-que-se-usan-en-el-test">Elementos que se usan en el test</h3>
<h4 id="anotaciones-de-spring-y-junit">Anotaciones de Spring y JUnit:</h4>
<ul>
<li><code>@Autowired</code>: le indica a Spring que inyecte automáticamente una instancia de <code>MockMvc</code> en el test.</li>
<li><code>@Test</code>: anotación de JUnit 5 que marca un método como test y permite que el framework lo ejecute durante la fase de testing.</li>
<li><code>@AutoConfigureMockMvc</code>: indica a Spring Boot que configure automáticamente <code>MockMvc</code>, permitiéndonos simular peticiones HTTP a los endpoints sin arrancar un servidor real.</li>
</ul>
<h4 id="herramientas-para-simular-peticiones-http">Herramientas para simular peticiones HTTP:</h4>
<ul>
<li><code>MockMvc</code>: utilidad de Spring para simular llamadas HTTP (GET, POST, etc.) contra los controladores y verificar sus respuestas.</li>
<li><code>get()</code>: construye una petición HTTP de tipo GET contra la ruta indicada, en nuestro ejemplo <code>/hello</code>.</li>
<li><code>perform()</code>: ejecuta la petición HTTP simulada y definida previamente. Devuelve el resultado para poder hacer comprobaciones sobre la respuesta.</li>
</ul>
<h4 id="validación-de-la-respuesta">Validación de la respuesta:</h4>
<ul>
<li><code>andExpect()</code>: define una expectativa sobre la respuesta obtenida; si no se cumple, el test falla.</li>
<li><code>status()</code> e <code>isOk()</code>: comprueban que el código de estado HTTP de la respuesta sea 200 (OK).</li>
<li><code>content()</code>: permite validar el contenido del cuerpo de la respuesta.</li>
</ul>
<h1 id="ejecutar-los-test-con-maven">Ejecutar los test con Maven</h1>
<p>Una vez configurados los tests, podemos ejecutarlos desde el propio IDE o, al igual que para la construcción del artefacto, desde la línea de comandos utilizando Maven.</p>
<p>Desde un terminal, situados en la carpeta raíz del proyecto, ejecutamos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn test</span></span></code></pre></div>
<p>Maven compilará el código y lanzará todos los tests definidos en el proyecto, mostrando el resultado en la consola.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado positivo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ok.png"></p>
<p>Si forzamos un error -por ejemplo, cambiando el texto esperado en el cuerpo de la respuesta- y ejecutamos de nuevo el comando, veremos cómo el test falla. La salida por consola indicará qué test ha fallado, qué se esperaba que ocurriera y cuál ha sido el resultado real, proporcionando información muy útil para entender el problema y corregirlo.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado negativo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ko.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>En este artículo hemos dado el primer paso en la incorporación de tests automatizados a nuestra API Spring Boot. Partiendo de un ejemplo muy sencillo, hemos visto cómo <strong>validar que la aplicación arranca correctamente y cómo comprobar el comportamiento de un endpoint HTTP mediante tests automatizados</strong>.</p>
<p>Hasta ahora, en la serie hemos creado una API básica, la hemos compilado con Maven y la hemos preparado para su ejecución. Con estos primeros tests añadimos <strong>una capa fundamental de seguridad que nos permitirá evolucionar el código con mayor confianza</strong>.</p>
<p>En los siguientes artículos iremos ampliando este enfoque, incorporando tests más completos y sentando las bases para su integración en un proceso de integración continua (CI), donde estos tests se ejecutarán automáticamente en cada cambio del código.</p>
]]></content:encoded></item><item><title>Spring Boot Hello World</title><link>/posts/spring-boot-hello-world/</link><pubDate>Thu, 01 Jan 2026 12:29:15 +0100</pubDate><guid>/posts/spring-boot-hello-world/</guid><description>&lt;p&gt;&lt;a href="https://spring.io/projects/spring-boot"&gt;Spring Boot&lt;/a&gt; es en la actualidad un &lt;strong&gt;estándar en el desarrollo de aplicaciones Java&lt;/strong&gt;, con mucho protagonismo en el mundo empresarial. Con una &lt;strong&gt;configuración inicial simplificada e inteligente&lt;/strong&gt; permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de &lt;em&gt;starters&lt;/em&gt; que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.&lt;/p&gt;
&lt;h1 id="objetivo"&gt;Objetivo&lt;/h1&gt;
&lt;p&gt;Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de &lt;em&gt;Hello World&lt;/em&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://spring.io/projects/spring-boot">Spring Boot</a> es en la actualidad un <strong>estándar en el desarrollo de aplicaciones Java</strong>, con mucho protagonismo en el mundo empresarial. Con una <strong>configuración inicial simplificada e inteligente</strong> permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de <em>starters</em> que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.</p>
<h1 id="objetivo">Objetivo</h1>
<p>Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de <em>Hello World</em>.</p>
<h1 id="creación-del-proyecto-base">Creación del proyecto base</h1>
<p>El ecosistema Spring cuenta con una herramienta que facilita la creación de la estructura base del proyecto, se trata de <a href="https://start.spring.io/">Spring initializr</a>.</p>
<p>Ésta genera la estructura básica de un proyecto Spring Boot en segundos. Permite seleccionar la versión de Spring, el lenguaje, el <em>build system</em> (Maven o Gradle) y las dependencias necesarias, creando automáticamente un proyecto listo para abrir en cualquier IDE.</p>
<p>Para el <em>Hello World</em> solo es necesario incluir la dependencia de <code>Spring Web</code>.</p>
<p><img alt="Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr.png"></p>
<p>Al pulsar en <em>GENERATE</em> se descarga un fichero comprimido con el proyecto que cuenta con la estructura y componentes mínimos para importar en cualquier IDE y desplegar la aplicación.</p>
<p><img alt="Proyecto generado con Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr-project.png"></p>
<p>Este proyecto se puede importar o abrir desde cualquier IDE que cuente con soporte para desarrollo de aplicaciones Spring Boot. Para el ejemplo, se utiliza IntelliJ.</p>
<p><img alt="Aplicación generada por Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-app.png"></p>
<h1 id="creación-del-controlador">Creación del controlador</h1>
<p>Un controlador en Spring <strong>es el componente encargado de recibir las peticiones HTTP, procesarlas y devolver una respuesta</strong>. Actúa como punto de entrada de la aplicación, mapeando URLs y métodos HTTP (GET, POST, etc.) a métodos Java. Separa la lógica de exposición de la API del resto de la aplicación y su lógica, que se conformará con otro tipo de componentes que se utilizarán para obtener respuestas más complejas.</p>
<h2 id="anotaciones-restcontroller-y-getmapping">Anotaciones RestController y GetMapping</h2>
<p>Las anotaciones en Spring <strong>son etiquetas que se colocan sobre clases y métodos para decirle al framework qué papel cumple cada uno y cómo deben actuar</strong>, sin necesidad de escribir código adicional de configuración.</p>
<h3 id="restcontroller">RestController</h3>
<p>Es la anotación que marca una clase como <strong>un componente que expone endpoints REST</strong>. Todo lo que devuelvan sus métodos será serializado automáticamente (Strings, JSONs, etc.).</p>
<h3 id="getmapping">GetMapping</h3>
<p>Asocia un método a <strong>una petición HTTP GET y a una ruta concreta</strong> que se debe especificar como un parámetro de la propia anotación.</p>
<h2 id="ejemplo-completo">Ejemplo completo</h2>
<p>Para disponer de un endpoint bajo la ruta <code>/hello</code> que responda a una petición GET, bastaría con completar el Controlador <code>HelloController</code> de la siguiente forma:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@RestController</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloController</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="prueba-de-la-aplicación">Prueba de la aplicación</h1>
<p>Para probar la aplicación y ver el <em>HelloWorld</em> bastaría con ejecutar en nuestro IDE la clase anotada con <code>SpringBootApplication</code>.</p>
<p>En la consola veremos aparecer varios logs que nos indicarán:</p>
<ul>
<li>Inicialización del servidor web (Tomcat por defecto) y el puerto en el que estará escuchando para recibir las peticiones (8080 por defecto)</li>
<li>Inicialización correcta de la propia aplicación.</li>
</ul>
<p>En ese momento, podremos abrir un navegador web e intentar cargar la url <code>http://localhost:8080/hello</code> lo cual realiza por defecto una petición GET a la ruta, devolviendo en la pantalla el texto de <em>HelloWorld</em>.</p>
<p><img alt="Hello World con Spring Boot en el navegador" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-hello.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Montar un <em>Hello World</em> en Spring Boot es rápido y sencillo, y un buen punto de partida para entender las piezas que lo hacen posible. Las anotaciones son uno de los pilares del framework, y dominar su significado permite construir aplicaciones más limpias, expresivas y mantenibles.</p>
]]></content:encoded></item></channel></rss>