<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amin Rashidbeigi</title>
    <description>The latest articles on DEV Community by Amin Rashidbeigi (@aminrashidbeigi).</description>
    <link>https://dev.to/aminrashidbeigi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1012782%2F0a899177-6392-4492-b98a-5e45b460e452.jpeg</url>
      <title>DEV Community: Amin Rashidbeigi</title>
      <link>https://dev.to/aminrashidbeigi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aminrashidbeigi"/>
    <language>en</language>
    <item>
      <title>My Thoughts on The Clean Coder Book</title>
      <dc:creator>Amin Rashidbeigi</dc:creator>
      <pubDate>Wed, 30 Oct 2024 18:16:43 +0000</pubDate>
      <link>https://dev.to/aminrashidbeigi/my-thoughts-on-the-clean-coder-book-5ano</link>
      <guid>https://dev.to/aminrashidbeigi/my-thoughts-on-the-clean-coder-book-5ano</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Some time ago, I was searching for a book related to software but not purely technical, making it easier for me to read. I was looking for something I could enjoy even with a busy mind after work.&lt;/p&gt;

&lt;p&gt;After checking my Goodreads to-read list, I decided to read The Clean Coder and started right away. The book's introduction hooked me, and I ended up reading it to the end.&lt;/p&gt;

&lt;p&gt;To clarify, this post is not a review. There are already plenty of reviews for this book. This post is simply some of my thoughts on topics that were particularly interesting or debatable for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saying No, at Any Cost
&lt;/h2&gt;

&lt;p&gt;For a software engineer or a manager, it's common to face situations where a product manager or stakeholders ask to meet a deadline without enough time or resources. In these situations, many of us, including myself before reading this book, respond with “I will try.”&lt;/p&gt;

&lt;p&gt;But this response creates an expectation that might not align with the reality that the task may not meet the deadline. By saying this, we often work overtime or on weekends, accepting significant risks, which I written in more details in the post &lt;a href="https://aminrb.me/managing-overdue-tasks/" rel="noopener noreferrer"&gt;Managing Overdue Tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Uncle Bob states in Chapter 2 that you have to say no at any cost. It is not reasonable to say, “Okay, I will try.” However, sometimes the decision is already made, and you cannot alter it. In this scenario, I believe it's more pragmatic to communicate the situation to your manager or stakeholders as much as possible. They might have some incorrect assumptions about the technical details. After that, as engineers, we should try to find and offer alternatives to meet the deadline, even if it means cutting some corners. And to make it clear, by saying cutting corners I don’t mean reducing writing unit tests or deploying carelessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know the Basics
&lt;/h2&gt;

&lt;p&gt;Over time, the abstraction in software engineering has increased. With higher abstraction, there's much less need to know the basics of our field. If you have a computer science background, you've already studied most of them, but if you're from another field, you're likely to miss some.&lt;/p&gt;

&lt;p&gt;Even though I studied computer engineering, I've forgotten a lot and sometimes only remember the titles.&lt;/p&gt;

&lt;p&gt;But it's still important to read or review them. Despite hundreds of new ideas in our field each year, we can still improve our thinking and creativity by strengthening our computer knowledge muscles. Maybe there's no need to implement a merge sort for the rest of your life (except in interviews) with new AI tools, but the idea behind merge sort is still valid.&lt;/p&gt;

&lt;p&gt;Uncle Bob recommends a list of areas that every software professional should be familiar with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Design patterns. You ought to be able to describe all 24 patterns in the GOF book and have a working knowledge of many of the patterns in the POSA books.&lt;/li&gt;
&lt;li&gt;Design principles. You should know the SOLID principles and have a good understanding of the component principles.&lt;/li&gt;
&lt;li&gt;Methods. You should understand XP, Scrum, Lean, Kanban, Waterfall, Structured Analysis, and Structured Design.&lt;/li&gt;
&lt;li&gt;Disciplines. You should practice TDD, Object-Oriented design, Structured Programming, Continuous Integration, and Pair Programming.&lt;/li&gt;
&lt;li&gt;Artifacts: You should know how to use: UML, DFDs, Structure Charts, Petri Nets, State Transition Diagrams and Tables, flow charts, and decision tables.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;By doing these kinds of exercises, our computer muscles become more powerful, and I believe that at some points in our careers, this will help us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice at Home, Perform at Your Job
&lt;/h2&gt;

&lt;p&gt;I remember periods in my career when I didn't learn much at work. I got upset and thought it had to be a short-term issue, so I tried to learn on my own. I read books and did some side projects like the &lt;a href="https://aminrb.me/side-projects/#-expedition-diaries/" rel="noopener noreferrer"&gt;Expedition Diaries&lt;/a&gt;. Although I might not have been wrong, the idea of "Practice at Home, Perform at Your Job" changed my mindset.&lt;/p&gt;

&lt;p&gt;Uncle Bob compares programmers to musicians. A musician always practices to learn new things during training and at home, and when performing in a concert, they use their knowledge and skills without expecting to learn new things. As programmers, we should use our knowledge at work and not expect our employer to provide learning opportunities. If they do, that's great. But we shouldn't expect it because they already pay us for doing our job, and it's not their responsibility to teach us.&lt;/p&gt;

&lt;p&gt;This changed my mind because now, if I find myself in a situation where I can't learn from my current job, I know it's not my employer’s responsibility. It's mine. By accepting this, I can make better decisions without complaining. I can do side projects, read books, or even change my job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know the Domain of Your Product
&lt;/h2&gt;

&lt;p&gt;Another interesting topic the book discusses, and one that was a reminder for me, is knowing the domain of our team, project, and company. Developers write code for a product. By understanding the product domain and its metrics, it’s easier to collaborate with product people and stakeholders. It gives us the power to make better trade-offs and long-term decisions for the product.&lt;/p&gt;

&lt;p&gt;Uncle Bob recommends that when joining a team, you should read some books, talk to experts, or talk to customers to get a better understanding of the domain.&lt;/p&gt;

&lt;p&gt;In my experience, product owners and managers prefer to work with developers who understand the domain and help them make better decisions rather than those who just accept everything and act like coding machines. This is a huge benefit for every product team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;"The Clean Coder" is a simple and easy-to-read book that I enjoyed a lot. Although there were some parts I didn't fully agree with, it offered new and helpful content for me.&lt;/p&gt;

&lt;p&gt;If you are a beginner in your career, reading this book can definitely help you. If you are an experienced coder, there are topics that may still interest you.&lt;/p&gt;




&lt;p&gt;This post was originally published at &lt;a href="https://aminrb.me/the-clean-coder/" rel="noopener noreferrer"&gt;My Thoughts on The Clean Coder Book&lt;/a&gt;. If you liked this post and want to see more, follow me at &lt;a href="https://x.com/AminRashidbeigi" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>unclebob</category>
      <category>cleancoder</category>
      <category>software</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>When to Start Secret Rotation</title>
      <dc:creator>Amin Rashidbeigi</dc:creator>
      <pubDate>Wed, 30 Oct 2024 18:13:23 +0000</pubDate>
      <link>https://dev.to/aminrashidbeigi/when-to-start-secret-rotation-hao</link>
      <guid>https://dev.to/aminrashidbeigi/when-to-start-secret-rotation-hao</guid>
      <description>&lt;p&gt;Have you ever accidentally pushed a sensitive password and realized how difficult it is to ensure no one gains access to it? Or have you mistakenly logged an API token, and now it's scattered across your logging system?&lt;/p&gt;

&lt;p&gt;In these cases, the first thing you'd do is change the secret. But what if you never caught the log in the first place? Or how can you be sure no one else has pushed sensitive data? This is where &lt;strong&gt;Secret Rotation&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Secret Rotation?
&lt;/h2&gt;

&lt;p&gt;Secret rotation is the practice of regularly updating or replacing sensitive credentials used in applications and systems. These credentials could be anything from passwords and API keys to encryption keys, certificates, or other authentication tokens. The process can be either manual or automated using secret management tools like &lt;a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Start Secret Rotation?
&lt;/h2&gt;

&lt;p&gt;You might think secret rotation is something only big companies with many employees or years of experience need to worry about. However, waiting too long to implement it can make it too expensive to adopt. Secrets often spread across different projects and teams, making it costly and complicated to change a single secret.&lt;/p&gt;

&lt;p&gt;Once, while working on a project, I was given a key with high-level access to modify data I didn't need. When I communicated it with my manager, I discovered that many others used the same key across multiple projects. Changing it became a considerable challenge because not only did every user need to update their keys, but we also had to identify all the projects using this key. If we disabled it, many things could stop working. The complexity and risk made it challenging to take action, forcing the team to explore other out-of-the-box solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Start Secret Rotation Early?
&lt;/h2&gt;

&lt;p&gt;We've realized that starting secret rotation late can make it hard to change. But there are other benefits to starting early as well.&lt;/p&gt;

&lt;p&gt;If a team starts using secret rotation early, it becomes part of the culture. The idea that secrets should be easy to change gets built into the development process. The team will always keep in mind that the product should allow for easy secret changes. This makes automation easier, and developers feel more comfortable admitting they accidentally committed sensitive data because it can be changed easily.&lt;/p&gt;

&lt;p&gt;While no system can ever be 100% secure, secret rotation helps reduce the risk of data leaks, especially when they go unnoticed.&lt;/p&gt;




&lt;p&gt;This post was originally published at &lt;a href="https://aminrb.me/secret-rotation/" rel="noopener noreferrer"&gt;When to Start Secret Rotation&lt;br&gt;
&lt;/a&gt;. If you liked this post and want to see more, follow me at &lt;a href="https://x.com/AminRashidbeigi" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>secretrotation</category>
    </item>
    <item>
      <title>Run Llama 3 Locally</title>
      <dc:creator>Amin Rashidbeigi</dc:creator>
      <pubDate>Wed, 30 Oct 2024 18:08:37 +0000</pubDate>
      <link>https://dev.to/aminrashidbeigi/run-llama-3-locally-2hk7</link>
      <guid>https://dev.to/aminrashidbeigi/run-llama-3-locally-2hk7</guid>
      <description>&lt;p&gt;In the generative AI era and with large language models, they say the only limitation is your imagination. So, as maybe you, I had some ideas to implement with LLMs, but I didn’t want to spend a lot on testing and validating my prototypes. So, I decided to run &lt;a href="https://www.llama.com/" rel="noopener noreferrer"&gt;Llama&lt;/a&gt;, the Meta open-source LLM, on my machine or a VM to implement my prototypes with a free and self-hosted LLM.&lt;/p&gt;

&lt;p&gt;This post is a guide on how to run Llama locally, step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need to Get Started
&lt;/h2&gt;

&lt;p&gt;At first, you need some computational power, which I assume you already have. You could use your MacBook or any other laptop or PC. Or you could host it somewhere else with more computational power. GPUs are more suitable for running these tasks, so if you need to get results faster, hosting it on a machine with a GPU would be better.&lt;/p&gt;

&lt;p&gt;Then, all you need is a tool called Ollama. Ollama is a tool to set up, download, and run large language models more easily than before. I tested it, and it was super easy to use and convenient, so I decided to introduce it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up and Running Ollama
&lt;/h2&gt;

&lt;p&gt;Now, we want to run Ollama on our machine. The whole process is straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Download and Install Ollama
&lt;/h3&gt;

&lt;p&gt;First, you need to download Ollama. You can download it through &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;their website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can choose the version that is most convenient for you. As a general rule, the more parameters, the bigger the download and the more time and resources it will take to provide the result.&lt;/p&gt;

&lt;p&gt;Once it is installed, you can run the &lt;code&gt;serve&lt;/code&gt; command to ensure that it works and is ready to be served:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Pull the Llama3 Model
&lt;/h3&gt;

&lt;p&gt;Now, you must select a model and download it to your local. You can check models &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For example, we want to download the llama3.2. You can download it with the ollama pull command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will take a while based on the model size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Generate Your First Self-Hosted LLM Result
&lt;/h3&gt;

&lt;p&gt;Now everything is ready! you can use ollama command line tool by this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run llama3.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4qj21gfz3atnridqiwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4qj21gfz3atnridqiwq.png" alt="Image description" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You either can send requests to the locolhost with your desired tool! like curl, postman, or within a python script or any other tools.&lt;/p&gt;

&lt;p&gt;Note: for writing the post, I am using the latest version of ollama as of now which is &lt;a href="https://github.com/ollama/ollama/releases/tag/v0.3.14" rel="noopener noreferrer"&gt;v0.3.14&lt;/a&gt;. Maybe contributors change some of those commands or even workflows. In this case, check their documentation for the latest changes, and please let me know in the comments to make this post up to date.&lt;/p&gt;




&lt;p&gt;This post originally published at &lt;a href="https://aminrb.me/run-llama-locally/" rel="noopener noreferrer"&gt;Run Llama 3 Locally&lt;/a&gt;. If you liked this post and want to see more, follow me at &lt;a href="https://x.com/AminRashidbeigi" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>llama</category>
      <category>ollama</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>Managing Overdue Tasks</title>
      <dc:creator>Amin Rashidbeigi</dc:creator>
      <pubDate>Mon, 25 Mar 2024 15:04:36 +0000</pubDate>
      <link>https://dev.to/aminrashidbeigi/managing-overdue-tasks-5id</link>
      <guid>https://dev.to/aminrashidbeigi/managing-overdue-tasks-5id</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Getting better at estimating how long tasks will take is a skill that develops as you gain more experience in your career, especially when working with a team or company for a long time. However, even with careful planning, unexpected challenges can pop up, causing delays and potentially missing deadlines.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Common Approach: Finish It at All Costs
&lt;/h2&gt;

&lt;p&gt;When faced with a deadline, the first solution many of us consider is to work extra hours or through the weekend. We might think that failing to complete the task on time means we've let others down, broken a promise, or even fear it could reflect negatively on our next performance review.&lt;/p&gt;

&lt;p&gt;But this strategy, even if it helps in the short term, isn't healthy and leads to several problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Higher Chances of Mistakes:&lt;/strong&gt; Rushing to finish and working without enough rest increases the likelihood of errors. I'm sure that most people will create more bugs when they haven't had a good night's sleep compared to those who have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Quality and Higher Costs:&lt;/strong&gt; To meet deadlines, we might skip necessary steps such as writing good tests, doing enough manual testing, or making quick fixes that don't align with the project's overall design. These "technical debts" can lead to potential problems and costs in the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missed Learning Opportunities:&lt;/strong&gt; Failing to meet an estimate is a chance to learn and improve. However, if we just work longer hours without discussing the issue with the team, we miss out on this learning. Plus, it sets unrealistic expectations for future projects, leaving us in a similar situation again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Better Strategy: Communicate
&lt;/h2&gt;

&lt;p&gt;The most effective way to handle these situations is to talk to your team or project manager as soon as you realize the task might not meet the deadline. This could be during a daily check-in, a message, or the next scheduled meeting. Communicate the challenges clearly and make sure everyone understands the situation.&lt;/p&gt;

&lt;p&gt;Next steps can include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adjusting the Plan:&lt;/strong&gt; The manager may agree to extend the deadline once they understand the situation, allowing you to complete the work without undue stress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addressing Immediate Deadlines:&lt;/strong&gt; If the deadline can't be moved, you can discuss the issues highlighted earlier (like the risk of errors and lower quality) to help decide the best course of action together.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Meeting deadlines is important, but we should also think about how our choices affect things in the long run. Trying to finish things fast or working too hard might seem like good ideas at first, but they can lead to more mistakes, lower-quality work, and missed chances for the team to learn and grow.&lt;/p&gt;

&lt;p&gt;Talking openly about any problems and being clear about delays help make a better work environment and lead to a more lasting way of managing projects.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aminrb.me/managing-overdue-tasks/"&gt;Managing Overdue Tasks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Go Test Doubles by Example</title>
      <dc:creator>Amin Rashidbeigi</dc:creator>
      <pubDate>Tue, 02 Jan 2024 07:27:56 +0000</pubDate>
      <link>https://dev.to/aminrashidbeigi/go-test-doubles-by-example-3lb9</link>
      <guid>https://dev.to/aminrashidbeigi/go-test-doubles-by-example-3lb9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When writing tests for a program, we often need to consider dependencies and external APIs that we prefer not to call every time the test runs. Calling these external dependencies not only adds complexity to our tests but also incurs costs. To address this issue, we can use test doubles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Test Doubles?
&lt;/h2&gt;

&lt;p&gt;Test doubles offer an alternative to real dependencies. While we commonly refer to all of them as mocks, there are various test doubles, each serving different purposes.&lt;/p&gt;

&lt;p&gt;Let's briefly review each type of test double:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fake&lt;/strong&gt;: Fakes are actual implementations of dependencies, in a simpler and chipper way. Consider a scenario where we have a database dependency. We can create a fake in-memory database that implements the database interface, allowing us to use it as a substitute for the actual database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dummy&lt;/strong&gt;: Dummies act as placeholder objects. We don't utilize their functionalities; instead, we pass parameters just to fulfill test requirements. We don't want to test their logic or behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock&lt;/strong&gt;: Sometimes we want to test the behavior of our logic rather than the implementation or output. Mocks are for this purpose. For instance, ensuring a specific API called only once per request can be achieved by mocking that API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stub&lt;/strong&gt;: When testing particular logic, we might need to assess the program's state rather than its behavior. Stubs come into play by generating predefined responses for specific parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spy&lt;/strong&gt;: Spies, similar to stubs, offer dynamic behavior based on how they're invoked, providing a more interactive aspect. We can say that spies inherit from both stubs and mocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're unfamiliar with test doubles and want to read more about them, check the resources I've referenced at the end of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Doubles by an Example
&lt;/h2&gt;

&lt;p&gt;Now that we've reviewed all test doubles, let's explain each of them using a real-world Go example.&lt;/p&gt;

&lt;p&gt;Imagine we have a Payment service that depends on a payment gateway and contains a &lt;code&gt;ProcessPayment&lt;/code&gt; method implementing logic when a user submits a payment. We want to write some test double for the payment package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payment Gateway Dependency
&lt;/h3&gt;

&lt;p&gt;First of all, let's define a simple implementation of an example gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"errors"&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;RealPaymentGateway&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RealPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid payment amount"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Payment Processor
&lt;/h3&gt;

&lt;p&gt;Now, let's create a simple implementation of the &lt;code&gt;PaymentProcessor&lt;/code&gt;. A &lt;code&gt;gateway&lt;/code&gt; is as an input for this processor, and it includes a &lt;code&gt;Process&lt;/code&gt; function that calls the &lt;code&gt;ProcessPayment&lt;/code&gt; method of the gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGateway&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentProcessor&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;gateway&lt;/span&gt; &lt;span class="n"&gt;PaymentGateway&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt; &lt;span class="n"&gt;PaymentGateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's initialize our program with the &lt;code&gt;main&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aminrashidbeigi/go-test-doubles/gateway"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aminrashidbeigi/go-test-doubles/payment"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;realPaymentGateway&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RealPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; 
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;realPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;100.0&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment processed successfully!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run this program we get this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment processed successfully!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, our program works well. Now, we've reached the fun part – writing tests!&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Doubles Implementations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Dummy
&lt;/h4&gt;

&lt;p&gt;As I said, dummy just pass parameters to initialize a dependency and we don’t expect anything from them. In this example, the &lt;code&gt;PaymentGatewayDummy&lt;/code&gt; satisfies the &lt;code&gt;PaymentGateway&lt;/code&gt; interface by implementing the &lt;code&gt;ProcessPayment&lt;/code&gt; method but does nothing inside the method. It's simply there to fulfill the requirement of a &lt;code&gt;PaymentGateway&lt;/code&gt; object for the tests that rely on this interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewayDummy&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayDummy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithDummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayDummy&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected no error, got %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Fake
&lt;/h4&gt;

&lt;p&gt;In the payment system example, the &lt;code&gt;PaymentGatewayFake&lt;/code&gt; provides an alternative implementation with simpler logic. Here's the fake implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewayFake&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayFake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid amount"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithFake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayFake&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected an error for zero amount, got none"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;errNegative&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errNegative&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected an error for negative amount, got none"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Mock
&lt;/h4&gt;

&lt;p&gt;Mocks are used to define and enforce expectations on interactions with an object. They specify certain method calls with specific parameters and help verify that these interactions occur as expected during tests. In this example, the mock records each call made to &lt;code&gt;ProcessPayment&lt;/code&gt;, allowing later verification to ensure that the expected method calls happened in the specified order and with the correct parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewayMock&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;calls&lt;/span&gt;      &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;callAmount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ProcessPayment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;VerifyCalls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedCalls&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedCalls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;expectedCalls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayMock&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;75.0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;expectedCalls&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"ProcessPayment"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VerifyCalls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedCalls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected calls %v, got %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expectedCalls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callAmount&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected call amount %f, got %f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paymentGatewayMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Stub
&lt;/h4&gt;

&lt;p&gt;The stub here represents a simplified version of the &lt;code&gt;PaymentGateway&lt;/code&gt;. It has a &lt;code&gt;success&lt;/code&gt; boolean field that determines whether the payment should be successful or fail. The &lt;code&gt;ProcessPayment&lt;/code&gt; method of the stub checks the &lt;code&gt;success&lt;/code&gt; field and returns an error if &lt;code&gt;success&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;, simulating a failed payment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewayStub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayStub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"payment failed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithStub_SuccessfulPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayStub&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected no error, got %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithStub_FailedPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewayStub&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected an error, got none"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Spy
&lt;/h4&gt;

&lt;p&gt;The spy here is used to track and record information about interactions with the &lt;code&gt;PaymentGateway&lt;/code&gt;. It has &lt;code&gt;called&lt;/code&gt; and &lt;code&gt;amount&lt;/code&gt; fields to record whether the &lt;code&gt;ProcessPayment&lt;/code&gt; method has been called and the amount passed to it. During testing, the spy captures and records this information, allowing tests to assert whether certain interactions have occurred.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PaymentGatewaySpy&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;called&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewaySpy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ProcessPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;called&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPaymentProcessor_WithSpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;paymentGatewaySpy&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PaymentGatewaySpy&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;paymentProcessor&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentGatewaySpy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;75.0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;paymentGatewaySpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;called&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected PaymentGatewaySpy to be called"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;paymentGatewaySpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expected amount %f, got %f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paymentGatewaySpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Each type of test double—dummies, stubs, spies, mocks, and fakes—has its unique role in testing of our code. They help us check if our code works correctly, create specific situations for testing, and make sure our tests happen in a safe space with reduced complexity and cost.&lt;/p&gt;

&lt;p&gt;I've placed all this code in a GitHub repository. You can check it here: &lt;a href="https://github.com/aminrashidbeigi/go-test-double"&gt;Go Test Double&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, you can follow &lt;a href="https://twitter.com/AminRashidbeigi"&gt;my Twitter&lt;/a&gt; for future posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://martinfowler.com/bliki/TestDouble.html"&gt;TestDouble&lt;/a&gt; - Martin Fowler&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://martinfowler.com/articles/mocksArentStubs.html"&gt;Mocks Aren't Stubs&lt;/a&gt; - Martin Fowler&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://xunitpatterns.com/Test%20Double.html"&gt;Test Double&lt;/a&gt; - XUnitPatterns&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aminrb.me/go-test-double/"&gt;Go Test Doubles by Example&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
