{"id":49865936,"url":"https://github.com/lukasniessen/tdd-bdd-explained","last_synced_at":"2026-05-15T03:00:20.972Z","repository":{"id":291979135,"uuid":"979415362","full_name":"LukasNiessen/tdd-bdd-explained","owner":"LukasNiessen","description":"TDD and BDD explained with code examples","archived":false,"fork":false,"pushed_at":"2025-05-07T14:03:40.000Z","size":16,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-05-15T03:00:04.795Z","etag":null,"topics":["bdd","tdd","test","testing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LukasNiessen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-07T13:29:09.000Z","updated_at":"2026-02-27T11:54:08.000Z","dependencies_parsed_at":"2025-05-07T14:45:19.392Z","dependency_job_id":"e13559bc-1bca-4c3b-8c47-d59b706e51c5","html_url":"https://github.com/LukasNiessen/tdd-bdd-explained","commit_stats":null,"previous_names":["lukasniessen/tdd-bdd"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LukasNiessen/tdd-bdd-explained","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Ftdd-bdd-explained","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Ftdd-bdd-explained/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Ftdd-bdd-explained/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Ftdd-bdd-explained/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LukasNiessen","download_url":"https://codeload.github.com/LukasNiessen/tdd-bdd-explained/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasNiessen%2Ftdd-bdd-explained/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33051875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-15T02:00:06.351Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bdd","tdd","test","testing"],"created_at":"2026-05-15T03:00:06.959Z","updated_at":"2026-05-15T03:00:20.956Z","avatar_url":"https://github.com/LukasNiessen.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TDD and BDD Explained\n\n_TDD = Test-Driven Development_  \n_BDD = Behavior-Driven Development_\n\n## Behavior-Driven Development\n\nBDD is all about the following mindset: **Do not test code. Test behavior.**\n\nSo it's a shift of the testing mindset. This is why in BDD, we also introduced new terms:\n\n- **Test suites** become **specifications**,\n- **Test cases** become **scenarios**,\n- We don't **test code**, we **verify behavior**.\n\nLet's make this clear by an example.\n\n## Java Example\n\nIf you are not familiar with Java, look in the repo files for other languages (I've added: Java, Python, JavaScript, C#, Ruby, Go).\n\n```java\npublic class UsernameValidator {\n\n    public boolean isValid(String username) {\n        if (isTooShort(username)) {\n            return false;\n        }\n        if (isTooLong(username)) {\n            return false;\n        }\n        if (containsIllegalChars(username)) {\n            return false;\n        }\n        return true;\n    }\n\n    boolean isTooShort(String username) {\n        return username.length() \u003c 3;\n    }\n\n    boolean isTooLong(String username) {\n        return username.length() \u003e 20;\n    }\n\n    // allows only alphanumeric and underscores\n    boolean containsIllegalChars(String username) {\n        return !username.matches(\"^[a-zA-Z0-9_]+$\");\n    }\n}\n```\n\nUsernameValidator checks if a username is valid (3-20 characters, alphanumeric and \\_). It returns true if all checks pass, else false.\n\nHow to test this? Well, if we test if the code does what it does, it might look like this:\n\n```java\n@Test\npublic void testIsValidUsername() {\n    // create spy / mock\n    UsernameValidator validator = spy(new UsernameValidator());\n\n    String username = \"User@123\";\n    boolean result = validator.isValidUsername(username);\n\n    // Check if all methods were called with the right input\n    verify(validator).isTooShort(username);\n    verify(validator).isTooLong(username);\n    verify(validator).containsIllegalCharacters(username);\n\n    // Now check if they return the correct thing\n    assertFalse(validator.isTooShort(username));\n    assertFalse(validator.isTooLong(username));\n    assertTrue(validator.containsIllegalCharacters(username));\n}\n```\n\nThis is not great. What if we change the logic inside isValidUsername? Let's say we decide to replace `isTooShort()` and `isTooLong()` by a new method `isLengthAllowed()`?\n\n**The test would break**. Because it almost mirros the implementation. Not good. The test is now tightly coupled to the implementation.\n\nIn BDD, we just verify the behavior. So, in this case, we just check if we get the wanted outcome:\n\n```java\n@Test\nvoid shouldAcceptValidUsernames() {\n    // Examples of valid usernames\n    assertTrue(validator.isValidUsername(\"abc\"));\n    assertTrue(validator.isValidUsername(\"user123\"));\n    ...\n}\n\n@Test\nvoid shouldRejectTooShortUsernames() {\n    // Examples of too short usernames\n    assertFalse(validator.isValidUsername(\"\"));\n    assertFalse(validator.isValidUsername(\"ab\"));\n    ...\n}\n\n@Test\nvoid shouldRejectTooLongUsernames() {\n    // Examples of too long usernames\n    assertFalse(validator.isValidUsername(\"abcdefghijklmnopqrstuvwxyz\"));\n    ...\n}\n\n@Test\nvoid shouldRejectUsernamesWithIllegalChars() {\n    // Examples of usernames with illegal chars\n    assertFalse(validator.isValidUsername(\"user@name\"));\n    assertFalse(validator.isValidUsername(\"special$chars\"));\n    ...\n}\n```\n\nMuch better. If you change the implementation, the tests will not break. They will work as long as the method works.\n\nImplementation is irrelevant, we only specified our wanted behavior. This is why, in BDD, we don't call it a _test suite_ but we call it a _specification_.\n\nOf course this example is very simplified and doesn't cover all aspects of BDD but it clearly illustrates the core of BDD: **testing code vs verifying behavior**.\n\n## Is it about tools?\n\nMany people think BDD is something written in Gherkin syntax with tools like Cucumber or SpecFlow:\n\n```gherkin\nFeature: User login\n  Scenario: Successful login\n    Given a user with valid credentials\n    When the user submits login information\n    Then they should be authenticated and redirected to the dashboard\n```\n\nWhile these tools are great and definitely help to implement BDD, it's not limited to them. BDD is much broader. BDD is about behavior, not about tools. You can use BDD with these tools, but also with other tools. Or without tools at all.\n\n## More on BDD\n\nhttps://www.youtube.com/watch?v=Bq_oz7nCNUA (by Dave Farley)  \nhttps://www.thoughtworks.com/en-de/insights/decoder/b/behavior-driven-development (Thoughtworks)\n\n---\n\n## Test-Driven Development\n\nTDD simply means: Write tests first! Even before writing the any code.\n\nSo we write a test for something that was not yet implemented. And yes, of course that test will fail. This may sound odd at first but TDD follows a simple, iterative cycle known as Red-Green-Refactor:\n\n- **Red**: Write a failing test that describes the desired functionality.\n- **Green**: Write the minimal code needed to make the test pass.\n- **Refactor**: Improve the code (and tests, if needed) while keeping all tests passing, ensuring the design stays clean.\n\nThis cycle ensures that every piece of code is justified by a test, reducing bugs and improving confidence in changes.\n\n## Three Laws of TDD\n\nRobert C. Martin (Uncle Bob) formalized TDD with three key rules:\n\n- You are not allowed to write any production code unless it is to make a failing unit test pass.\n- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.\n- You are not allowed to write any more production code than is sufficient to pass the currently failing unit test.\n\n## TDD in Action\n\nFor a practical example, check out this video of Uncle Bob, where he is coding live, using TDD: https://www.youtube.com/watch?v=rdLO7pSVrMY\n\nIt takes time and practice to \"master TDD\".\n\n## Combine them (TDD + BDD)!\n\nTDD and BDD complement each other. It's best to use both.\n\nTDD ensures your code is correct by driving development through failing tests and the Red-Green-Refactor cycle. BDD ensures your tests focus on what the system should do, not how it does it, by emphasizing behavior over implementation.\n\nWrite TDD-style tests to drive small, incremental changes (Red-Green-Refactor). Structure those tests with a BDD mindset, specifying behavior in clear, outcome-focused scenarios.\nThis approach yields code that is:\n\n- Correct: TDD ensures it works through rigorous testing.\n- Maintainable: BDD's focus on behavior keeps tests resilient to implementation changes.\n- Well-designed: The discipline of writing tests first encourages modularity, loose coupling, and clear separation of concerns.\n\n## Another Example of BDD\n\nLastly another example.\n\nNon-BDD:\n\n```java\n@Test\npublic void testHandleMessage() {\n    Publisher publisher = new Publisher();\n    List\u003cBuilderList\u003e builderLists = publisher.getBuilderLists();\n    List\u003cLog\u003e logs = publisher.getLogs();\n\n    Message message = new Message(\"test\");\n    publisher.handleMessage(message);\n\n    // Verify build was created\n    assertEquals(1, builderLists.size());\n    BuilderList lastBuild = getLastBuild(builderLists);\n    assertEquals(\"test\", lastBuild.getName());\n    assertEquals(2, logs.size());\n}\n```\n\nWith BDD:\n\n```java\n@Test\npublic void shouldGenerateAsyncMessagesFromInterface() {\n    Interface messageInterface = Interfaces.createFrom(SimpleMessageService.class);\n    PublisherInterface publisher = new PublisherInterface(messageInterface, transport);\n\n    // When we invoke a method on the interface\n    SimpleMessageService service = publisher.createPublisher();\n    service.sendMessage(\"Hello\");\n\n    // Then a message should be sent through the transport\n    verify(transport).send(argThat(message -\u003e\n        message.getMethod().equals(\"sendMessage\") \u0026\u0026\n        message.getArguments().get(0).equals(\"Hello\")\n    ));\n}\n```\n\n# Feedback ⌨️😊\n\nFeel free to contribute by submitting a PR or creating an issue.  \n**If this was helpful, you can show support by giving this repository a star! 🌟**\n\n# License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasniessen%2Ftdd-bdd-explained","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukasniessen%2Ftdd-bdd-explained","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasniessen%2Ftdd-bdd-explained/lists"}