{"id":13501477,"url":"https://github.com/laffra/auger","last_synced_at":"2025-03-29T09:30:34.398Z","repository":{"id":57412584,"uuid":"73340920","full_name":"laffra/auger","owner":"laffra","description":"Automated Unittest Generation for Python","archived":false,"fork":false,"pushed_at":"2020-06-19T19:27:37.000Z","size":103,"stargazers_count":199,"open_issues_count":8,"forks_count":27,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-03-03T05:36:20.331Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/laffra.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-10T02:43:21.000Z","updated_at":"2024-12-06T21:36:36.000Z","dependencies_parsed_at":"2022-08-24T22:12:20.821Z","dependency_job_id":null,"html_url":"https://github.com/laffra/auger","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laffra%2Fauger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laffra%2Fauger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laffra%2Fauger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laffra%2Fauger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/laffra","download_url":"https://codeload.github.com/laffra/auger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246166975,"owners_count":20734376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T22:01:38.841Z","updated_at":"2025-03-29T09:30:34.057Z","avatar_url":"https://github.com/laffra.png","language":"Python","readme":"# Auger\nAuger is a project to automatically generate unit tests for Python code.\n\nSee\n[these slides](http://goo.gl/PuZsgX)\nor\n[this blog](http://chrislaffra.blogspot.com/2016/12/auger-automatic-unit-test-generation.html)\nentry for more information.\n\n# Installation\n\nInstall auger with:\n\n    pip install auger-python\n\n# Running Auger\n    \nTo generate a unit test for any class or module, for Python 2 or 3, do this:\n\n```python\nimport auger\n\nwith auger.magic([ \u003cany list of modules or classes\u003e ]):\n    \u003cany code that exercises your application\u003e\n```\n\n# A Simple Example\n\nHere is a simple example that does not rely on Auger at all:\n\n```python\nclass Foo:                # Declare a class with a method\n    def bar(self, x):\n        return 2 * x      # Duplicate x and return it\n\ndef main():\n    foo = Foo()           # Create an instance of Foo\n    print(foo.bar(32))    # Call the bar method and print the result\n\nmain()\n```\n\nInside the `main` function we call the `bar` method and it will print 64.\n\n# Running Auger on our Simple Example\n\nTo generate a unit test for this class, we run the code again, but this time in the context of Auger:\n\n```python\nimport auger\n\nwith auger.magic([Foo]):\n    main()\n```\n\nThis will print out the following:\n\n    64\n    Auger: generated test: tests/test_Foo.py\n\nThe test that is generated looks like this, with some imports and test for main removed:\n\n```python\nimport unittest\n\nclass FooTest(unittest.TestCase):\n    def test_bar(self):\n        foo_instance = Foo()\n        self.assertEquals(\n            foo_instance.bar(x=32),\n            64\n        )\n\nif __name__ == \"__main__\":\n    unittest.main()\n```\n\n# Running Auger in verbose mode\n\nRather than emit tests in the file system, Auger can also print out the test to the console,\nby using the `verbose` parameter:\n\n```python\nimport auger\n\nwith auger.magic([Foo], verbose=True):\n    main()\n```\n\nIn that case, Auger will not generate any tests, but just print them out.\n\n# A larger example\n\nConsider the following example, `pet.py`, included in the `sample` folder, that lets us create a `Pet` with a name and a species:\n\n```python\nfrom animal import Animal\n\nclass Pet(Animal):\n  def __init__(self, name, species):\n    Animal.__init__(self, species)\n    self.name = name\n\n  def getName(self):\n    return self.name\n\n  def __str__(self):\n    return \"%s is a %s\" % (self.getName(), self.getSpecies())\n\ndef createPet(name, species):\n  return Pet(name, species)\n```\n\nA `Pet` is really a special kind of `Animal`, with a name, which is defined in `animal.py`:\n\n```python\nclass Animal(object):\n  def __init__(self, species):\n    self.species = species\n\n  def getSpecies(self):\n    return self.species\n```\n\nWith those two definitions, we can create a `Pet` instance and print out some details:\n\n```python\nimport animal\nimport pet\n\ndef main():\n  p = pet.createPet(\"Polly\", \"Parrot\")\n  print(p, p.getName(), p.getSpecies())\n  \nmain()\n```\n\nThis produces:\n\n    Polly is a Parrot Polly Parrot\n\n# Calling Auger on our larger example\n\nWith Auger, we can record all calls to all functions and methods defined in `pet.py`,\nwhile also remembering the details for all calls going out from `pet.py` to other modules,\nso they can be mocked out.\n\nInstead of saying:\n\n```python\nif __name__ == \"__main__\":\n  main()\n```\n\nWe would say:\n\n```python\nimport auger\n\nif __name__ == \"__main__\":\n  with auger.magic([pet]):   # this is the new line and invokes Auger\n    main()\n```\n\nThis produces the following automatically generated unit test for `pet.py`:\n\n```python\nfrom mock import patch\nfrom sample.animal import Animal\nimport sample.pet\nfrom sample.pet import Pet\nimport unittest\n\n\nclass PetTest(unittest.TestCase):\n    @patch.object(Animal, 'get_species')\n    @patch.object(Animal, 'get_age')\n    def test___str__(self, mock_get_age, mock_get_species):\n        mock_get_age.return_value = 12\n        mock_get_species.return_value = 'Dog'\n        pet_instance = Pet('Clifford', 'Dog', 12)\n        self.assertEquals(pet_instance.__str__(), 'Clifford is a dog aged 12')\n\n    def test_create_pet(self):\n        self.assertIsInstance(sample.pet.create_pet(age=12,species='Dog',name='Clifford'), Pet)\n\n    def test_get_name(self):\n        pet_instance = Pet('Clifford', 'Dog', 12)\n        self.assertEquals(pet_instance.get_name(), 'Clifford')\n\n    def test_lower(self):\n        self.assertEquals(Pet.lower(s='Dog'), 'dog')\n\nif __name__ == \"__main__\":\n    unittest.main()\n```\n\nNote that auger detects object creation, method invocation, and static methods. It automatically\ngenerate mocks for `Animal`. The mock for `get_species` returns 'Dog' and `get_age` returns 12. \nNamely, those were the values Auger recorded when we ran our sample code the last time.\n\n# Benefits of Auger\n\nBy automatically generating unit tests, we dramatically cut down the cost of software\ndevelopment. The tests themselves are intended to help developers get going on their unit testing\nand lower the learning curve for how to write tests.\n\n# Known limitations of Auger\n\nAuger does not do try to substitue parameters with synthetic values such as `-1`, `None`, or `[]`. \nAuger also does not act well when code uses exceptions. Auger also does not like methods that have a decorator.\n\nAuger only records a given execution run and saves the run as a test. Auger does not know if the code actually\nworks as intended. If the code contains a bug, Auger will simply record the buggy behavior. There is no free\nlunch here. It is up to the developer to verify the code actually works.\n","funding_links":[],"categories":["Python","Tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaffra%2Fauger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaffra%2Fauger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaffra%2Fauger/lists"}