{"id":18286842,"url":"https://github.com/khesualdo/unit-testing","last_synced_at":"2025-04-12T08:16:30.541Z","repository":{"id":91122617,"uuid":"171410379","full_name":"khesualdo/Unit-Testing","owner":"khesualdo","description":":lemon: :cherries: :watermelon: My cheatsheet for writing unit tests with Moq.NET and MSTest","archived":false,"fork":false,"pushed_at":"2019-03-29T23:02:11.000Z","size":40,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-05T13:28:40.102Z","etag":null,"topics":["csharp","dotnet","microsoft","moq","moq-framework","mstest","testing","unit-testing"],"latest_commit_sha":null,"homepage":"","language":null,"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/khesualdo.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}},"created_at":"2019-02-19T05:22:49.000Z","updated_at":"2023-12-14T11:21:37.000Z","dependencies_parsed_at":"2023-11-12T02:15:06.406Z","dependency_job_id":null,"html_url":"https://github.com/khesualdo/Unit-Testing","commit_stats":null,"previous_names":["khesualdo/unit-testing"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khesualdo%2FUnit-Testing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khesualdo%2FUnit-Testing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khesualdo%2FUnit-Testing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khesualdo%2FUnit-Testing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khesualdo","download_url":"https://codeload.github.com/khesualdo/Unit-Testing/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230889102,"owners_count":18295641,"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":["csharp","dotnet","microsoft","moq","moq-framework","mstest","testing","unit-testing"],"created_at":"2024-11-05T13:22:29.930Z","updated_at":"2024-12-22T21:44:32.581Z","avatar_url":"https://github.com/khesualdo.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Unit-Testing\n\n### `Strict` vs. `Loose`\n\nStrict mocking means that we must set up expectations (`Verify`, `Returns`) on all members of a mock object otherwise an exception is thrown. Loose mocking on the other hand does not require explicit expectations on all class members, instead default values are returned when no expectation is explicitly declared.\n\nDefault behaviour in Moq is loose mocking.\n\n```C#\ninterface IGuidUtility{\n\n  CreateGuid();\n  DeleteGuid();\n}\n  \nclass Utility{\n\n  IGuidUtility _guidUtility;\n  \n  void DoSomething(){\n    _guidUtility.CreateGuid();\n    \n    ...\n    \n    _guidUtility.DeleteGuid();\n  }\n}\n\n// This test will pass in loose mode\n// This test will fail in strict mode, because we did not set up expectations for all members of the `moq`\n// Specifically, we did not setup expectation for `DeleteGuid()`\n[Test]\nmoq = new Mock\u003cIGuidUtility\u003e(MockBehavior.Strict);\nutility = new Utility(moq.Object);\nmoq.Setup(x =\u003e x.CreateGuid()).Returns(...);\n\n// This test will pass in loose mode\n// This test will pass in strict mode, because we setup expectations for all members of the `moq`\n[Test]\nmoq = new Mock\u003cIGuidUtility\u003e(MockBehavior.Strict);\nutility = new Utility(moq.Object);\nmoq.Setup(x =\u003e x.CreateGuid()).Returns(...);\nmoq.Setup(x =\u003e x.DeleteGuid()).Returns(...);\n```\n\n### `Setup`\n\n#### High-level Steps\n1. Create an instance of Moq class that takes a interface or class with virtual methods as a generic parameter\n2. Tell the Mock what you want to happen, specifying parameters, and even return values\n3. Get back the mocked object which will behave in the way in which you told it to. Use this mocked object in your code.\n4. Call `Verify` or `VerifyAll` on the mocked object to ensure that what you said would happen actually happened. This step is not always required.\n\n#### Low-level Steps\n```C#\n// A either has to be an interface or an abstract class, \n// it may also be a regular class as long as B is a virtual method\nMock\u003cA\u003e mock = new Mock\u003cA\u003e(MockBehavior.Strict);\n\n// B either has to be a method declared in an interface or an abstract class, \n// or be a virtual method in a regular class\n// R is any valid object of return type of B\nmock.Setup(m =\u003e m.B(...)).Returns(R);\n\n// When B is called on mock.Object, R is returned\nValidateOpenIdConnectJSONWebToken(..., mock.Object, ...);\n```\n\nSuch specific properties are required from `A` and `B`, because Moq will create its own version of `B` (for that it either needs to extend `A` and override `B` or implement `A` and define `B`) that follows the requirements set by the `Setup` and `Returns` method calls.\n\n#### `It.IsAny\u003cGuid\u003e`\nAllows any value of type Guid to be used as a parameter.\n\n```C#\nmoq = new Mock\u003cIUtility\u003e(MockBehavior.Strict);\nmoq.Setup(m =\u003e m.CompareGuids(It.IsAny\u003cGuid\u003e, It.IsAny\u003cGuid\u003e, It.IsAny\u003cGuid\u003e)).Returns(True);\n\n// Case 1 - Pass\nmoq.Object.CompareGuids(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid());\n\n// Case 2 - Pass\nGuid sameGuid = Guid.NewGuid();\nmoq.Object.CompareGuids(sameGuid, sameGuid, sameGuid);\n\n// Case 3 - Pass\nmoq.Object.CompareGuids(Guid.Empty, Guid.NewGuid(), sameGuid);\n```\n\n#### `It.Is\u003cGuid\u003e`\nRestricts allowable parameters (of type Guid).\n\n```C#\nmoq.Setup(m =\u003e m.CompareGuids(\n  It.Is\u003cGuid\u003e(g =\u003e g.ToString().StartsWith(\"4\"), \n  It.Is\u003cGuid\u003e(g =\u003e g != Guid.Empty), \n  It.Is\u003cGuid\u003e(g =\u003e g == expectedGuid))\n).Returns(True);\n```\n\n#### Variable\nForces to pass the exact variable that was used for mocking.\n\n```C#\nGuid first = Guid.NewGuid();\nGuid second = Guid.NewGuid();\nGuid third = Guid.NewGuid();\n\nmoq.Setup(m =\u003e m.CompareGuids(first, second, third)).Returns(True);\n\n// Case 1 - Pass\nmoq.Object.CompareGuids(first, second, third);\n\n// Case 2 - Fail\nmoq.Object.CompareGuids(first, first, second);\n```\n\n### `Verify` vs. `VerifyAll` vs. `Assert`\n\n`Verify` is used for verifying expected behaviour.\n```C#\nvar mockFileWriter = new Mock\u003cIFileWriter\u003e();\nuseMockFileWriter(mockFileWriter.Object);\n\n// Verify that `WriteLine` was called exactly 1 time on `mockFileWriter` in `useMockFileWriter()`\nmockFileWriter.Verify(fw =\u003e fw.WriteLine(\"1001,10.53\"), Times.Exactly(1));\n```\n\n`VerifyAll` will verify that all `Setup` calls were called (eg. in this case will check that `CopyLine` was called on `mockFileWriter` in `useMockFileWriter()`) as well as verify expected behaviour.\n```C#\nvar mockFileWriter = new Mock\u003cIFileWriter\u003e();\nmockFileWriter.Setup(fw =\u003e fw.CopyLine(\"1001,10.53\")).Returns(\"1001,10.53\");\nmockFileWriter.Setup(fw =\u003e fw.WriteLine(\"1001,10.53\")).AtMostOnce();\nmockFileWriter.Setup(fw =\u003e fw.ReadLine()).AtMostOnce();\nuseMockFileWriter(mockFileWriter.Object);\nmockFileWriter.VerifyAll();\n```\n\n`Assert` is used for verifying expected result.\n```C#\nvar actual = DoSomething();\nvar expected = 1002;\nAssert.AreEqual(expected, actual);\n```\n\n### Exceptions\n\nAssert on exception message.\n```C#\ntry\n{\n  somethingThatFillThrowAnException();\n  Assert.Fail();\n}\ncatch (ArgumentException e)\n{\n  Assert.AreEqual(\"Erro message\", e.Message);\n}\ncatch (Exception)\n{\n  Assert.Fail();\n}\n```\n\nAssert on exception type.\n```C#\ntry\n{\n  somethingThatFillThrowAnException();\n  Assert.Fail();\n}\ncatch (Exception e)\n{\n  Assert.IsTrue(e is ArgumentException);\n}\n```\n\nAssert on exception type.\n```C#\n[TestMethod]\n[ExpectedException(typeof(ArgumentNullException))]\npublic void MethodTest()\n{\n  somethingThatFillThrowAnException();\n}\n```\n\n### `Protected` Methods\n\n\u003e `Protected()` is found in the `Moq.Protected` namespace. Also `ItExpr` is the same as `It`, but is used when mocking protected methods.\n\nThis example mocks `HttpClient`. The trick to mocking `HttpClient`, is to not mock it, but to mock `HttpMessageHandler` instead. `HttpClient` acts as a manager / wrapper for `HttpMessageHandler`, so calls to `DeleteAsync()`, `GetAsync()`, `PostAsync()`, `PutAsync()`, and `SendAsync()`, will all return the `HttpResponseMessage` you setup on the `SendAsync()` method of the `HttpMessageHandler` class.\n```C#\nMock\u003cHttpMessageHandler\u003e mockMessageHandler = new Mock\u003cHttpMessageHandler\u003e();\n\nmockMessageHandler.Protected()\n    .Setup\u003cTask\u003cHttpResponseMessage\u003e\u003e(\"SendAsync\", ItExpr.IsAny\u003cHttpRequestMessage\u003e(), ItExpr.IsAny\u003cCancellationToken\u003e())\n    .ReturnsAsync(\n        new HttpResponseMessage\n        {\n            StatusCode = HttpStatusCode.OK,\n            Content = new StringContent(\"Whatever content\")\n        }\n    );\n\nHttpClient httpClient = new HttpClient(mockMessageHandler.Object);\n\n// One thing to note is that response.IsSuccessStatusCode will be true only if 200 \u003e=response.StatusCode \u003c= 299\nHttpResponseMessage response = await httpClient.PostAsync(\"Any valid endpoint\", null);\nstring content = await response.Content.ReadAsStringAsync(); // Content is stored in ASCII\nHttpStatusCode statusCode = response.StatusCode;\n```\n\n### Partial Mock\n\nUsed when mocking the behaviour of a member (method or property) from the same test class. This technique will require making all the target members of the class you are testing public / protected and virtual.\n\nPartially mocking a class ignores parts of its behavior, this means the class is doing more than one thing. This violates the Single Responsibility Principle, and that is a code smell.\n\n\u003e Important to note that `mock.Object.someFunction();` will take us into the implementation of the method, because we are mocking a class. When mocking an interface default values will be returned.\n\n```C#\npublic MyClass{\n  public void someFunction(){\n    someOtherFunction();\n    someFunFunction();\n  }\n  \n  private void someOtherFunction(){ ... }\n  public void someFunFunction(){ ... }\n}\n\n// Change access to public / protected virtual\npublic MyClass{\n  public void someFunction(){\n    someOtherFunction();\n  }\n  \n  public virtual void someOtherFunction(){ ... }\n  public virtual void someFunFunction(){ ... }\n}\n\nMock\u003cMyClass\u003e mock = new Mock\u003cMyClass\u003e();\nmock.Setup(x =\u003e x.someOtherFunction()).Returns(\"Whatever I want\");\nmock.Setup(x =\u003e x.someFunFunction()).Returns(\"Whatever I want\");\nmock.Object.someFunction();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhesualdo%2Funit-testing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhesualdo%2Funit-testing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhesualdo%2Funit-testing/lists"}