├── .github └── workflows │ └── ci.yml ├── .gitignore ├── README.md ├── RestSharpWorkshop.odp ├── RestSharpWorkshop.pdf ├── RestSharpWorkshop.pptx ├── RestSharpWorkshop.sln └── RestSharpWorkshop ├── Answers ├── Answers01.cs ├── Answers02.cs ├── Answers03.cs ├── Answers04.cs ├── Answers05.cs └── Models │ ├── Account.cs │ └── GraphQLQuery.cs ├── Examples ├── Examples01.cs ├── Examples02.cs ├── Examples03.cs ├── Examples04.cs ├── Examples05.cs └── Models │ ├── Company.cs │ ├── GraphQLQuery.cs │ ├── Post.cs │ └── User.cs ├── Exercises ├── Exercises01.cs ├── Exercises02.cs ├── Exercises03.cs ├── Exercises04.cs ├── Exercises05.cs └── Models │ ├── Account.cs │ └── GraphQLQuery.cs ├── RestSharpWorkshop.csproj └── TestBase.cs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # based on https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 2 | 3 | name: Run Answers 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | 11 | concurrency: 12 | # For pull requests, cancel all currently-running jobs for this workflow 13 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | 20 | runs-on: ubuntu-latest 21 | strategy: 22 | matrix: 23 | dotnet-version: [6.0.x, 7.0.x, 8.0.x, 9.0.x] 24 | include: 25 | - dotnet-version: 6.0.x 26 | framework: net6.0 27 | - dotnet-version: 7.0.x 28 | framework: net7.0 29 | - dotnet-version: 8.0.x 30 | framework: net8.0 31 | - dotnet-version: 9.0.x 32 | framework: net9.0 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | 37 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} 38 | uses: actions/setup-dotnet@v3 39 | with: 40 | dotnet-version: ${{ matrix.dotnet-version }} 41 | 42 | - name: List installed .NET SDKs 43 | run: dotnet --list-sdks 44 | 45 | - name: Restore project dependencies 46 | run: dotnet restore -p:TargetFramework=${{ matrix.framework }} 47 | 48 | - name: Build (${{ matrix.framework }}) 49 | run: dotnet build --framework ${{ matrix.framework }} --no-restore 50 | 51 | - name: Run tests (${{ matrix.framework }}) 52 | run: dotnet test --framework ${{ matrix.framework }} --filter FullyQualifiedName~Answers --no-restore --no-build --verbosity normal 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RestSharp workshop 2 | ================== 3 | For those of you looking to gain some experience working with [RestSharp](https://restsharp.dev/), here are all the materials from a workshop I've created and delivered multiple times to good reviews. Feel free to use, share and adapt these materials as you see fit. 4 | 5 | What do I need? 6 | --- 7 | .NET 6, 7 or 8 and an IDE of your choice. That's it. 8 | 9 | What topics do you cover in this workshop? 10 | --- 11 | * Writing basic tests for REST APIs using RestSharp 12 | * Creating and running data driven tests using RestSharp and NUnit 13 | * (De-)serializing C# objects to/from JSON 14 | * Writing tests for GraphQL APIs 15 | 16 | What APIs are invoked in the tests? 17 | --- 18 | All API calls that you should make in the exercises are mocked using [WireMock.Net](https://github.com/WireMock-Net/WireMock.Net). All mock definitions are included in the project, so you can focus on learning RestSharp. 19 | 20 | Running the tests from the command line 21 | --- 22 | 23 | ```bash 24 | dotnet test 25 | ``` 26 | 27 | Slides 28 | --- 29 | The .pptx/.pdf/.odp file in the root folder contains all slides from the workshop. Again, feel free to use, share and adapt them to fit your own requirements. 30 | 31 | Comments? Saying thanks? 32 | --- 33 | Feel free to file an issue here or send me an email at bas@ontestautomation.com. 34 | 35 | I'd rather have you deliver the workshop instead... 36 | --- 37 | Sure, I'd love to. Again, send me an email and I'll be happy to discuss options. In house or at your conference, I'm sure we can work something out. 38 | -------------------------------------------------------------------------------- /RestSharpWorkshop.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basdijkstra/restsharp-workshop/56df72aa6e12019a03a79708a46609a91a300b4d/RestSharpWorkshop.odp -------------------------------------------------------------------------------- /RestSharpWorkshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basdijkstra/restsharp-workshop/56df72aa6e12019a03a79708a46609a91a300b4d/RestSharpWorkshop.pdf -------------------------------------------------------------------------------- /RestSharpWorkshop.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basdijkstra/restsharp-workshop/56df72aa6e12019a03a79708a46609a91a300b4d/RestSharpWorkshop.pptx -------------------------------------------------------------------------------- /RestSharpWorkshop.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30621.155 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharpWorkshop", "RestSharpWorkshop\RestSharpWorkshop.csproj", "{A1C8155F-4366-4794-BF5D-B92A90185C83}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A1C8155F-4366-4794-BF5D-B92A90185C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A1C8155F-4366-4794-BF5D-B92A90185C83}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A1C8155F-4366-4794-BF5D-B92A90185C83}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A1C8155F-4366-4794-BF5D-B92A90185C83}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {73437EC5-799F-455F-99AD-1FD897C3C9C8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Answers01.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Answers 8 | { 9 | [TestFixture] 10 | public class Answers01 : TestBase 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://localhost:9876"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | /** 25 | * Send a new GET request to /customer/12212 using the client defined above. 26 | * Check that the HTTP response code is equal to HttpStatusCode.OK. 27 | */ 28 | [Test] 29 | public async Task GetDataForCustomer12212_CheckStatusCode_ShouldBeHttpOK() 30 | { 31 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 32 | 33 | RestResponse response = await client.ExecuteAsync(request); 34 | 35 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 36 | } 37 | 38 | /** 39 | * Send a new GET request to /customer/12212 using the client defined above. 40 | * Check that the response content type is equal to 'application/json'. 41 | */ 42 | [Test] 43 | public async Task GetDataForCustomer12212_CheckContentType_ShouldContainApplicationJson() 44 | { 45 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 46 | 47 | RestResponse response = await client.ExecuteAsync(request); 48 | 49 | Assert.That(response.ContentType, Is.EqualTo("application/json")); 50 | } 51 | 52 | /** 53 | * Send a new GET request to /customer/12212 using the client defined above. 54 | * Check that the response contains a header 'Server' with value 'MockServer'. 55 | */ 56 | [Test] 57 | public async Task GetDataForCustomer12212_CheckServerHeader_ShouldBeMockServer() 58 | { 59 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 60 | 61 | RestResponse response = await client.ExecuteAsync(request); 62 | 63 | string serverHeaderValue = response.Server; 64 | 65 | Assert.That(serverHeaderValue, Is.EqualTo("MockServer")); 66 | } 67 | 68 | /** 69 | * Send a new GET request to /customer/12212 using the client defined above. 70 | * Check that the response contains a header 'MyHeader' with value 'MyHeaderValue'. 71 | */ 72 | [Test] 73 | public async Task GetDataForCustomer12212_CheckMyHeader_ShouldBeMyHeaderValue() 74 | { 75 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 76 | 77 | RestResponse response = await client.ExecuteAsync(request); 78 | 79 | string myHeader = response.GetHeaderValue("MyHeader"); 80 | 81 | Assert.That(myHeader, Is.EqualTo("MyHeaderValue")); 82 | } 83 | 84 | /** 85 | * Send a new GET request to /customer/12212 using the client defined above. 86 | * Check that the response body contains a JSON field 'firstName' with value 'John'. 87 | */ 88 | [Test] 89 | public async Task GetDataForCustomer12212_CheckFirstName_ShouldBeJohn() 90 | { 91 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 92 | 93 | RestResponse response = await client.ExecuteAsync(request); 94 | 95 | JObject responseData = JObject.Parse(response.Content); 96 | 97 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo("John")); 98 | } 99 | 100 | /** 101 | * Send a new GET request to /customer/12212 using the client defined above. 102 | * Check that the JSON field 'city', which is a child element of 'address', has value 'Beverly Hills'. 103 | */ 104 | [Test] 105 | public async Task GetDataForCustomer12212_CheckCityInAddress_ShouldBeBeverlyHills() 106 | { 107 | RestRequest request = new RestRequest("/customer/12212", Method.Get); 108 | 109 | RestResponse response = await client.ExecuteAsync(request); 110 | 111 | JObject responseData = JObject.Parse(response.Content); 112 | 113 | Assert.That(responseData.SelectToken("address.city").ToString(), Is.EqualTo("Beverly Hills")); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Answers02.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Answers 8 | { 9 | [TestFixture] 10 | public class Answers02 : TestBase 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://localhost:9876"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | /** 25 | * Refactor these three tests into a single, data driven test using the 26 | * [TestCase] attribute. 27 | * Add parameters to the test method and think about their data types. 28 | * Replace fixed values with parameterized values to make the tests data driven. 29 | */ 30 | [TestCase(12212, "John", TestName = "Customer 12212 is John")] 31 | [TestCase(12345, "Susan", TestName = "Customer 12345 is Susan")] 32 | [TestCase(12456, "Anna", TestName = "Customer 12456 is Anna")] 33 | public async Task GetDataFor_CheckFirstName_ShouldEqualExpectedName 34 | (int customerId, string expectedFirstName) 35 | { 36 | RestRequest request = new RestRequest($"/customer/{customerId}", Method.Get); 37 | 38 | RestResponse response = await client.ExecuteAsync(request); 39 | 40 | JObject responseData = JObject.Parse(response.Content); 41 | 42 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo(expectedFirstName)); 43 | } 44 | 45 | /** 46 | * Do the same, but now using the [TestCaseSource] attribute. Refer to the CustomerData 47 | * method defined below. 48 | */ 49 | [Test, TestCaseSource("CustomerData")] 50 | public async Task GetDataFor_CheckFirstName_ShouldEqualExpectedName_UsingTestCaseSource 51 | (int customerId, string expectedFirstName) 52 | { 53 | RestRequest request = new RestRequest($"/customer/{customerId}", Method.Get); 54 | 55 | RestResponse response = await client.ExecuteAsync(request); 56 | 57 | JObject responseData = JObject.Parse(response.Content); 58 | 59 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo(expectedFirstName)); 60 | } 61 | 62 | /** 63 | * Complete the body of this method to return the required test data: 64 | * | customerId | expectedFirstName | 65 | * | ---------- | ----------------- | 66 | * | 12212 | John | 67 | * | 12345 | Susan | 68 | * | 12456 | Anna | 69 | * Set the test name for each iteration using the .SetName() method. 70 | * Make sure to use a different test name compared to the previous exercise 71 | * to ensure that all iterations are seen as different tests! 72 | */ 73 | private static IEnumerable CustomerData() 74 | { 75 | yield return new TestCaseData(12212, "John"). 76 | SetName("Customer 12212 is John - using TestCaseSource"); 77 | yield return new TestCaseData(12345, "Susan"). 78 | SetName("Customer 12345 is Susan - using TestCaseSource"); 79 | yield return new TestCaseData(12456, "Anna"). 80 | SetName("Customer 12456 is Anna - using TestCaseSource"); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Answers03.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using RestSharp.Authenticators; 5 | using RestSharp.Authenticators.OAuth2; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | 9 | namespace RestSharpWorkshop.Answers 10 | { 11 | [TestFixture] 12 | public class Answers03 : TestBase 13 | { 14 | // The base URL for our example tests 15 | private const string BASE_URL = "http://localhost:9876"; 16 | 17 | /** 18 | * Create a new RestClient that uses basic authentication, 19 | * with username 'john' and password 'demo'. Pass in the BASE_URL 20 | * as a constructor argument. 21 | * 22 | * Use that client to perform a GET request to /token 23 | * 24 | * Extract the value of the 'token' element in the 25 | * response into a string variable 26 | * 27 | * Create another RestClient that uses OAuth2 authentication, 28 | * using the token you retrieved in the previous step. Pass in the BASE_URL 29 | * as a constructor argument here, too. 30 | * 31 | * Use the new RestClient to send a GET request to /secure/customer/12212 32 | * 33 | * Verify that the status code of this response is equal to HTTP 200 34 | */ 35 | [Test] 36 | public async Task GetTokenUsingBasicAuth_UseInOAuth2_CheckResponseStatusCode() 37 | { 38 | // Create the RestClient using basic authentication 39 | var options = new RestClientOptions(BASE_URL) 40 | { 41 | Authenticator = new HttpBasicAuthenticator("john", "demo") 42 | }; 43 | 44 | var client = new RestClient(options); 45 | 46 | // Perform the first request using basic auth 47 | RestRequest request = new RestRequest("/token", Method.Get); 48 | 49 | RestResponse response = await client.ExecuteAsync(request); 50 | 51 | JObject responseData = JObject.Parse(response.Content); 52 | 53 | // Store the token in a string 54 | string token = responseData.SelectToken("token").ToString(); 55 | 56 | // Create a new RestClient using OAuth2 authentication 57 | options = new RestClientOptions(BASE_URL) 58 | { 59 | Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(token, "Bearer") 60 | }; 61 | 62 | client = new RestClient(options); 63 | 64 | // Perform the second request using OAuth2 65 | request = new RestRequest("/secure/customer/12212", Method.Get); 66 | 67 | response = await client.ExecuteAsync(request); 68 | 69 | // Check that the status code is equal to 200 70 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Answers04.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using RestSharp; 3 | using RestSharpWorkshop.Answers.Models; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Answers 8 | { 9 | [TestFixture] 10 | public class Answers04 : TestBase 11 | { 12 | // The base URL for our tests 13 | private const string BASE_URL = "http://localhost:9876"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | /** 25 | * Create a new Account object with 'savings' as the account type and balance 0 26 | * 27 | * POST this object to /customer/12212/accounts 28 | * 29 | * Verify that the response HTTP status code is equal to 201 (Created) 30 | */ 31 | [Test] 32 | public async Task PostSavingsAccount_CheckStatusCode_ShouldEqual201() 33 | { 34 | Account account = new Account 35 | { 36 | Type = "savings", 37 | Balance = 0 38 | }; 39 | 40 | RestRequest request = new RestRequest($"/customer/12212/accounts", Method.Post); 41 | 42 | request.AddJsonBody(account); 43 | 44 | RestResponse response = await client.ExecuteAsync(request); 45 | 46 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Created)); 47 | } 48 | 49 | /** 50 | * Perform an HTTP GET to /customer/12212/account/12345 51 | * 52 | * Deserialize the response into an object of type Account 53 | * 54 | * Check that the value of the Balance property of this account is equal to 98765 55 | */ 56 | [Test] 57 | public async Task GetAccount_CheckBalance_ShouldEqual98765() 58 | { 59 | RestRequest request = new RestRequest($"/customer/12212/account/12345", Method.Get); 60 | 61 | RestResponse response = await client.ExecuteAsync(request); 62 | 63 | Account account = response.Data; 64 | 65 | Assert.That(account.Balance, Is.EqualTo(98765)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Answers05.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using NUnit.Framework; 4 | using RestSharp; 5 | using RestSharpWorkshop.Answers.Models; 6 | using System.Threading.Tasks; 7 | 8 | namespace RestSharpWorkshop.Answers 9 | { 10 | [TestFixture] 11 | public class Answers05 : TestBase 12 | { 13 | // The base URL for our tests 14 | private const string BASE_URL = "http://localhost:9876"; 15 | 16 | // The RestSharp client we'll use to make our requests 17 | private RestClient client; 18 | 19 | [OneTimeSetUp] 20 | public void SetupRestSharpClient() 21 | { 22 | client = new RestClient(BASE_URL); 23 | } 24 | 25 | /******************************************************* 26 | * Create a new GraphQLQuery object and use the given 27 | * query as the value for the Query property 28 | * 29 | * POST this object to /simple-graphql 30 | * 31 | * Assert that the name of the CEO is Elon Musk 32 | * 33 | * Use "data.company.ceo" as the argument to SelectToken() 34 | * to extract the required value from the response 35 | */ 36 | [Test] 37 | public async Task GetCompanyData_checkCeo_shouldBeElonMusk() 38 | { 39 | string query = "{ company { name ceo } }"; 40 | 41 | GraphQLQuery graphQLQuery = new GraphQLQuery 42 | { 43 | Query = query 44 | }; 45 | 46 | RestRequest request = new RestRequest("/simple-graphql", Method.Post); 47 | 48 | request.AddJsonBody(graphQLQuery); 49 | 50 | RestResponse response = await client.ExecuteAsync(request); 51 | 52 | JObject responseData = JObject.Parse(response.Content); 53 | 54 | Assert.That( 55 | responseData.SelectToken("data.company.ceo").ToString(), 56 | Is.EqualTo("Elon Musk") 57 | ); 58 | } 59 | 60 | /******************************************************* 61 | * Create a data driven test with three TestCase iterations: 62 | * ------------------------------------ 63 | * rocket id | rocket name | country 64 | * ------------------------------------ 65 | * falcon1 | Falcon 1 | Republic of the Marshall Islands 66 | * falconheavy | Falcon Heavy | United States 67 | * starship | Starship | United States 68 | * 69 | * Parameterize the test 70 | * 71 | * Create a new GraphQL query from the given query string 72 | * Pass in the rocket id as a variable value (the variable name is 'id') 73 | * 74 | * POST this object to /graphql-with-variables 75 | * 76 | * Assert that the name of the rocket is equal to the value in the TestCase 77 | * Use "data.rocket.name" as the argument to SelectToken() 78 | * to extract the required value from the response 79 | * 80 | * Also, assert that the country of the rocket is equal to the value in the TestCase 81 | * Use "data.rocket.country" as the argument to SelectToken() 82 | * to extract the required value from the response 83 | */ 84 | [TestCase("falcon1", "Falcon 1", "Republic of the Marshall Islands", TestName = "Falcon 1 was launched in the Republic of the Marshall Islands")] 85 | [TestCase("falconheavy", "Falcon Heavy", "United States", TestName = "Falcon Heavy was launched in the United States")] 86 | [TestCase("starship", "Starship", "United States", TestName = "Starship was launched in the United States")] 87 | public async Task getRocketDataById_checkNameAndCountry 88 | (string rocketId, string expectedName, string expectedCountry) 89 | { 90 | string query = @" 91 | query getRocketData($id: ID!) 92 | { 93 | rocket(id: $id) { 94 | name 95 | country 96 | } 97 | } 98 | "; 99 | 100 | var variables = new 101 | { 102 | id = rocketId 103 | }; 104 | 105 | GraphQLQuery graphQLQuery = new GraphQLQuery 106 | { 107 | Query = query, 108 | Variables = JsonConvert.SerializeObject(variables) 109 | }; 110 | 111 | RestRequest request = new RestRequest("/graphql-with-variables", Method.Post); 112 | 113 | request.AddJsonBody(graphQLQuery); 114 | 115 | RestResponse response = await client.ExecuteAsync(request); 116 | 117 | Assert.That((int)response.StatusCode, Is.EqualTo(200)); 118 | 119 | JObject responseData = JObject.Parse(response.Content); 120 | 121 | Assert.That( 122 | responseData.SelectToken("data.rocket.name").ToString(), 123 | Is.EqualTo(expectedName) 124 | ); 125 | 126 | Assert.That( 127 | responseData.SelectToken("data.rocket.country").ToString(), 128 | Is.EqualTo(expectedCountry) 129 | ); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Models/Account.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Answers.Models 4 | { 5 | public class Account 6 | { 7 | [JsonProperty("type")] 8 | public string Type { get; set; } 9 | [JsonProperty("balance")] 10 | public int Balance { get; set; } = 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Answers/Models/GraphQLQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Answers.Models 4 | { 5 | public class GraphQLQuery 6 | { 7 | [JsonProperty("query")] 8 | public string Query { get; set; } 9 | [JsonProperty("variables")] 10 | public string Variables { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Examples01.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Examples 8 | { 9 | [TestFixture] 10 | public class Examples01 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://jsonplaceholder.typicode.com"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | [Test] 25 | public async Task GetDataForUser1_CheckStatusCode_ShouldBeHttpOK() 26 | { 27 | RestRequest request = new RestRequest("/users/1", Method.Get); 28 | 29 | RestResponse response = await client.ExecuteAsync(request); 30 | 31 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 32 | } 33 | 34 | [Test] 35 | public async Task GetDataForUser1_CheckStatusCode_ShouldBeHttp200() 36 | { 37 | RestRequest request = new RestRequest("/users/1", Method.Get); 38 | 39 | RestResponse response = await client.ExecuteAsync(request); 40 | 41 | Assert.That((int)response.StatusCode, Is.EqualTo(200)); 42 | } 43 | 44 | [Test] 45 | public async Task GetDataForUser2_CheckContentType_ShouldBeApplicationJson() 46 | { 47 | RestRequest request = new RestRequest("/users/2", Method.Get); 48 | 49 | RestResponse response = await client.ExecuteAsync(request); 50 | 51 | Assert.That(response.ContentType, Does.Contain("application/json")); 52 | } 53 | 54 | [Test] 55 | public async Task GetDataForUser3_CheckServerHeader_ShouldBeCloudflare() 56 | { 57 | RestRequest request = new RestRequest("/users/3", Method.Get); 58 | 59 | RestResponse response = await client.ExecuteAsync(request); 60 | 61 | string serverHeaderValue = response.Server; 62 | 63 | Assert.That(serverHeaderValue, Is.EqualTo("cloudflare")); 64 | } 65 | 66 | [Test] 67 | public async Task GetDataForUser3_CheckXPoweredByHeader_ShouldBeExpress() 68 | { 69 | RestRequest request = new RestRequest("/users/3", Method.Get); 70 | 71 | RestResponse response = await client.ExecuteAsync(request); 72 | 73 | string serverHeaderValue = response.GetHeaderValue("X-Powered-By"); 74 | 75 | Assert.That(serverHeaderValue, Is.EqualTo("Express")); 76 | } 77 | 78 | [Test] 79 | public async Task GetDataForUser4_CheckName_ShouldBePatriciaLebsack() 80 | { 81 | RestRequest request = new RestRequest("/users/4", Method.Get); 82 | 83 | RestResponse response = await client.ExecuteAsync(request); 84 | 85 | JObject responseData = JObject.Parse(response.Content); 86 | 87 | Assert.That(responseData.SelectToken("name").ToString(), Is.EqualTo("Patricia Lebsack")); 88 | } 89 | 90 | [Test] 91 | public async Task GetDataForUser5_CheckCompanyName_ShouldBeKeeblerLLC() 92 | { 93 | RestRequest request = new RestRequest("/users/5", Method.Get); 94 | 95 | RestResponse response = await client.ExecuteAsync(request); 96 | 97 | JObject responseData = JObject.Parse(response.Content); 98 | 99 | Assert.That(responseData.SelectToken("company.name").ToString(), Is.EqualTo("Keebler LLC")); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Examples02.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Examples 8 | { 9 | [TestFixture] 10 | public class Examples02 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://jsonplaceholder.typicode.com"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | [TestCase(1, "Leanne Graham", TestName = "User 1 is Leanne Graham")] 25 | [TestCase(2, "Ervin Howell", TestName = "User 2 is Ervin Howell")] 26 | [TestCase(3, "Clementine Bauch", TestName = "User 3 is Clementine Bauch")] 27 | public async Task GetDataForUser_CheckName_ShouldEqualExpectedName_UsingTestCase 28 | (int userId, string expectedName) 29 | { 30 | RestRequest request = new RestRequest($"/users/{userId}", Method.Get); 31 | 32 | RestResponse response = await client.ExecuteAsync(request); 33 | 34 | JObject responseData = JObject.Parse(response.Content); 35 | 36 | Assert.That(responseData.SelectToken("name").ToString(), Is.EqualTo(expectedName)); 37 | } 38 | 39 | [TestCase(1, "Leanne Graham", TestName = "User 1 is Leanne Graham")] 40 | [TestCase(2, "Ervin Howell", TestName = "User 2 is Ervin Howell")] 41 | [TestCase(3, "Clementine Bauch", TestName = "User 3 is Clementine Bauch")] 42 | public async Task GetDataForUser_CheckName_ShouldEqualExpectedName_UsingTestCase_ExplicitPathSegment 43 | (int userId, string expectedName) 44 | { 45 | RestRequest request = new RestRequest("/users/{userId}", Method.Get); 46 | 47 | request.AddUrlSegment("userId", userId); 48 | 49 | RestResponse response = await client.ExecuteAsync(request); 50 | 51 | JObject responseData = JObject.Parse(response.Content); 52 | 53 | Assert.That(responseData.SelectToken("name").ToString(), Is.EqualTo(expectedName)); 54 | } 55 | 56 | [Test, TestCaseSource("UserData")] 57 | public async Task GetDataForUser_CheckName_ShouldEqualExpectedName_UsingTestCaseSource 58 | (int userId, string expectedName) 59 | { 60 | RestRequest request = new RestRequest($"/users/{userId}", Method.Get); 61 | 62 | RestResponse response = await client.ExecuteAsync(request); 63 | 64 | JObject responseData = JObject.Parse(response.Content); 65 | 66 | Assert.That(responseData.SelectToken("name").ToString(), Is.EqualTo(expectedName)); 67 | } 68 | 69 | private static IEnumerable UserData() 70 | { 71 | yield return new TestCaseData(1, "Leanne Graham"). 72 | SetName("User 1 is Leanne Graham - using TestCaseSource"); 73 | yield return new TestCaseData(2, "Ervin Howell"). 74 | SetName("User 2 is Ervin Howell - using TestCaseSource"); 75 | yield return new TestCaseData(3, "Clementine Bauch"). 76 | SetName("User 3 is Clementine Bauch - using TestCaseSource"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Examples03.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using RestSharp.Authenticators; 5 | using RestSharp.Authenticators.OAuth2; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | namespace RestSharpWorkshop.Examples 10 | { 11 | [TestFixture] 12 | public class Examples03 13 | { 14 | // The base URL for our example tests 15 | private const string BASE_URL = "http://jsonplaceholder.typicode.com"; 16 | 17 | [Test] 18 | public async Task SetBasicAuthentication() 19 | { 20 | var options = new RestClientOptions(BASE_URL) 21 | { 22 | Authenticator = new HttpBasicAuthenticator("username", "password") 23 | }; 24 | 25 | var client = new RestClient(options); 26 | } 27 | 28 | [Test] 29 | public async Task SetOAuth2Authentication() 30 | { 31 | var options = new RestClientOptions(BASE_URL) 32 | { 33 | Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("access_token", "Bearer") 34 | }; 35 | 36 | var client = new RestClient(options); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Examples04.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using RestSharp; 3 | using RestSharpWorkshop.Examples.Models; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Examples 8 | { 9 | [TestFixture] 10 | public class Examples04 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://jsonplaceholder.typicode.com"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | [Test] 25 | public async Task GetDataForUser1_CheckName_ShouldEqualLeanneGraham() 26 | { 27 | RestRequest request = new RestRequest($"/users/1", Method.Get); 28 | 29 | RestResponse response = await client.ExecuteAsync(request); 30 | 31 | User user = response.Data; 32 | 33 | Assert.That(user.Name, Is.EqualTo("Leanne Graham")); 34 | } 35 | 36 | [Test] 37 | public async Task PostNewPost_CheckStatusCode_ShouldBeHttpCreated() 38 | { 39 | Post post = new Post 40 | { 41 | UserId = 1, 42 | Title = "My new post title", 43 | Body = "This is the body of my new post" 44 | }; 45 | 46 | RestRequest request = new RestRequest($"/posts", Method.Post); 47 | 48 | request.AddJsonBody(post); 49 | 50 | RestResponse response = await client.ExecuteAsync(request); 51 | 52 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Created)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Examples05.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using NUnit.Framework; 4 | using RestSharp; 5 | using RestSharpWorkshop.Examples.Models; 6 | using System.Threading.Tasks; 7 | 8 | namespace RestSharpWorkshop.Examples 9 | { 10 | [TestFixture] 11 | public class Examples05 12 | { 13 | // The base URL for our example tests 14 | private const string BASE_URL = "https://graphql-weather-api.herokuapp.com/"; 15 | 16 | // The RestSharp client we'll use to make our requests 17 | private RestClient client; 18 | 19 | [OneTimeSetUp] 20 | public void SetupRestSharpClient() 21 | { 22 | client = new RestClient(BASE_URL); 23 | } 24 | 25 | [Test] 26 | public async Task GetWeatherForAmsterdam_CheckSummaryTitle() 27 | { 28 | string query = @" 29 | { 30 | getCityByName(name: ""Amsterdam"") { 31 | weather { 32 | summary { 33 | title 34 | } 35 | } 36 | } 37 | } 38 | "; 39 | 40 | GraphQLQuery graphQLQuery = new GraphQLQuery 41 | { 42 | Query = query, 43 | }; 44 | 45 | RestRequest request = new RestRequest("/", Method.Post); 46 | 47 | request.AddJsonBody(graphQLQuery); 48 | 49 | RestResponse response = await client.ExecuteAsync(request); 50 | 51 | JObject responseData = JObject.Parse(response.Content); 52 | 53 | Assert.That( 54 | responseData.SelectToken("data.getCityByName.weather.summary.title").ToString(), 55 | Is.EqualTo("Clouds") 56 | ); 57 | } 58 | 59 | [TestCase("Amsterdam", "Clouds", TestName = "In Amsterdam the weather is cloudy")] 60 | [TestCase("Berlin", "Clouds", TestName = "In Berlin the weather is cloudy")] 61 | [TestCase("Rome", "Clear", TestName = "In Rome the weather is clear")] 62 | public async Task GetWeatherForAmsterdam_CheckSummaryTitle_UsingParameterizedQuery 63 | (string city, string expectedWeather) 64 | { 65 | string query = @" 66 | query GetWeatherForCity($name: String!) 67 | { 68 | getCityByName(name: $name) { 69 | weather { 70 | summary { 71 | title 72 | } 73 | } 74 | } 75 | } 76 | "; 77 | 78 | var variables = new 79 | { 80 | name = city 81 | }; 82 | 83 | GraphQLQuery graphQLQuery = new GraphQLQuery 84 | { 85 | Query = query, 86 | Variables = JsonConvert.SerializeObject(variables) 87 | }; 88 | 89 | RestRequest request = new RestRequest("/", Method.Post); 90 | 91 | request.AddJsonBody(graphQLQuery); 92 | 93 | RestResponse response = await client.ExecuteAsync(request); 94 | 95 | JObject responseData = JObject.Parse(response.Content); 96 | 97 | Assert.That( 98 | responseData.SelectToken("data.getCityByName.weather.summary.title").ToString(), 99 | Is.EqualTo(expectedWeather) 100 | ); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Models/Company.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Examples.Models 4 | { 5 | public class Company 6 | { 7 | [JsonProperty("name")] 8 | public string Name { get; set; } 9 | [JsonProperty("catchPhrase")] 10 | public string CatchPhrase { get; set; } 11 | [JsonProperty("bs")] 12 | public string BS { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Models/GraphQLQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Examples.Models 4 | { 5 | public class GraphQLQuery 6 | { 7 | [JsonProperty("query")] 8 | public string Query { get; set; } 9 | [JsonProperty("variables")] 10 | public string Variables { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Models/Post.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Examples.Models 4 | { 5 | public class Post 6 | { 7 | [JsonProperty("userId")] 8 | public int UserId { get; set; } 9 | [JsonProperty("title")] 10 | public string Title { get; set; } 11 | [JsonProperty("body")] 12 | public string Body { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Examples/Models/User.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Examples.Models 4 | { 5 | public class User 6 | { 7 | [JsonProperty("id")] 8 | public int Id { get; set; } 9 | [JsonProperty("name")] 10 | public string Name { get; set; } 11 | [JsonProperty("username")] 12 | public string Username { get; set; } 13 | [JsonProperty("company")] 14 | public Company Company { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Exercises01.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | 8 | namespace RestSharpWorkshop.Exercises 9 | { 10 | [TestFixture] 11 | public class Exercises01 : TestBase 12 | { 13 | // The base URL for our example tests 14 | private const string BASE_URL = "http://localhost:9876"; 15 | 16 | // The RestSharp client we'll use to make our requests 17 | private RestClient client; 18 | 19 | [OneTimeSetUp] 20 | public void SetupRestSharpClient() 21 | { 22 | client = new RestClient(BASE_URL); 23 | } 24 | 25 | /** 26 | * Send a new GET request to /customer/12212 using the client defined above. 27 | * Check that the HTTP response code is equal to HttpStatusCode.OK. 28 | */ 29 | [Test] 30 | public async Task GetDataForCustomer12212_CheckStatusCode_ShouldBeHttpOK() 31 | { 32 | } 33 | 34 | /** 35 | * Send a new GET request to /customer/12212 using the client defined above. 36 | * Check that the response content type is equal to 'application/json'. 37 | */ 38 | [Test] 39 | public async Task GetDataForCustomer12212_CheckContentType_ShouldContainApplicationJson() 40 | { 41 | } 42 | 43 | /** 44 | * Send a new GET request to /customer/12212 using the client defined above. 45 | * Check that the response contains a header 'Server' with value 'MockServer'. 46 | */ 47 | [Test] 48 | public async Task GetDataForCustomer12212_CheckServerHeader_ShouldBeMockServer() 49 | { 50 | } 51 | 52 | /** 53 | * Send a new GET request to /customer/12212 using the client defined above. 54 | * Check that the response contains a header 'MyHeader' with value 'MyHeaderValue'. 55 | */ 56 | [Test] 57 | public async Task GetDataForCustomer12212_CheckMyHeader_ShouldBeMyHeaderValue() 58 | { 59 | } 60 | 61 | /** 62 | * Send a new GET request to /customer/12212 using the client defined above. 63 | * Check that the response body contains a JSON field 'firstName' with value 'John'. 64 | */ 65 | [Test] 66 | public async Task GetDataForCustomer12212_CheckFirstName_ShouldBeJohn() 67 | { 68 | } 69 | 70 | /** 71 | * Send a new GET request to /customer/12212 using the client defined above. 72 | * Check that the JSON field 'city', which is a child element of 'address', has value 'Beverly Hills'. 73 | */ 74 | [Test] 75 | public async Task GetDataForCustomer12212_CheckCityInAddress_ShouldBeBeverlyHills() 76 | { 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Exercises02.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Exercises 8 | { 9 | [TestFixture] 10 | public class Exercises02 : TestBase 11 | { 12 | // The base URL for our example tests 13 | private const string BASE_URL = "http://localhost:9876"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | /** 25 | * Refactor these three tests into a single, data driven test using the 26 | * [TestCase] attribute. 27 | * Add parameters to the test method and think about their data types. 28 | * Replace fixed values with parameterized values to make the tests data driven. 29 | */ 30 | [Test] 31 | public async Task GetDataForCustomer12212_CheckFirstName_ShouldBeJohn() 32 | { 33 | RestRequest request = new RestRequest($"/customer/12212", Method.Get); 34 | 35 | RestResponse response = await client.ExecuteAsync(request); 36 | 37 | JObject responseData = JObject.Parse(response.Content); 38 | 39 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo("John")); 40 | } 41 | 42 | [Test] 43 | public async Task GetDataForCustomer12345_CheckFirstName_ShouldBeSusan() 44 | { 45 | RestRequest request = new RestRequest($"/customer/12345", Method.Get); 46 | 47 | RestResponse response = await client.ExecuteAsync(request); 48 | 49 | JObject responseData = JObject.Parse(response.Content); 50 | 51 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo("Susan")); 52 | } 53 | 54 | [Test] 55 | public async Task GetDataForCustomer12456_CheckFirstName_ShouldBeAnna() 56 | { 57 | RestRequest request = new RestRequest($"/customer/12456", Method.Get); 58 | 59 | RestResponse response = await client.ExecuteAsync(request); 60 | 61 | JObject responseData = JObject.Parse(response.Content); 62 | 63 | Assert.That(responseData.SelectToken("firstName").ToString(), Is.EqualTo("Anna")); 64 | } 65 | 66 | /** 67 | * Do the same, but now using the [TestCaseSource] attribute. Refer to the CustomerData 68 | * method defined below. 69 | */ 70 | [Test] 71 | public async Task GetDataFor_CheckPlace_ShouldEqualExpectedPlace_UsingTestCaseSource() 72 | { 73 | // Copy the test method body from the previous exercise, 74 | // you should be able to reuse it without changes. 75 | } 76 | 77 | /** 78 | * Complete the body of this method to return the required test data: 79 | * | customerId | expectedFirstName | 80 | * | ---------- | ----------------- | 81 | * | 12212 | John | 82 | * | 12345 | Susan | 83 | * | 12456 | Anna | 84 | * Set the test name for each iteration using the .SetName() method. 85 | * Make sure to use a different test name compared to the previous exercise 86 | * to ensure that all iterations are seen as different tests! 87 | */ 88 | private static IEnumerable CustomerData() 89 | { 90 | return null; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Exercises03.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using NUnit.Framework; 3 | using RestSharp; 4 | using RestSharp.Authenticators.OAuth2; 5 | using RestSharp.Authenticators; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | 9 | namespace RestSharpWorkshop.Exercises 10 | { 11 | [TestFixture] 12 | public class Exercises03 : TestBase 13 | { 14 | // The base URL for our example tests 15 | private const string BASE_URL = "http://localhost:9876"; 16 | 17 | /** 18 | * Create a new RestClient that uses basic authentication, 19 | * with username 'john' and password 'demo'. Pass in the BASE_URL 20 | * as a constructor argument. 21 | * 22 | * Use that client to perform a GET request to /token 23 | * 24 | * Extract the value of the 'token' element in the 25 | * response into a string variable 26 | * 27 | * Create another RestClient that uses OAuth2 authentication, 28 | * using the token you retrieved in the previous step. Pass in the BASE_URL 29 | * as a constructor argument here, too. 30 | * 31 | * Use the new RestClient to send a GET request to /secure/customer/12212 32 | * 33 | * Verify that the status code of this response is equal to HTTP 200 34 | */ 35 | [Test] 36 | public async Task GetTokenUsingBasicAuth_UseInOAuth2_CheckResponseStatusCode() 37 | { 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Exercises04.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using RestSharp; 3 | using RestSharpWorkshop.Exercises.Models; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace RestSharpWorkshop.Exercises 8 | { 9 | [TestFixture] 10 | public class Exercises04 : TestBase 11 | { 12 | // The base URL for our tests 13 | private const string BASE_URL = "http://localhost:9876"; 14 | 15 | // The RestSharp client we'll use to make our requests 16 | private RestClient client; 17 | 18 | [OneTimeSetUp] 19 | public void SetupRestSharpClient() 20 | { 21 | client = new RestClient(BASE_URL); 22 | } 23 | 24 | /** 25 | * Create a new Account object with 'savings' as the account type and balance 0 26 | * 27 | * POST this object to /customer/12212/accounts 28 | * 29 | * Verify that the response HTTP status code is equal to 201 (Created) 30 | */ 31 | [Test] 32 | public async Task PostSavingsAccount_CheckStatusCode_ShouldEqual201() 33 | { 34 | Account account = new Account(); 35 | account.Balance = 0; 36 | RestRequest request = new RestRequest($"/customer/12212/accounts", Method.Post); 37 | RestResponse response = await client.ExecuteAsync(request); 38 | Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Created)); 39 | } 40 | 41 | /** 42 | * Perform an HTTP GET to /customer/12212/account/12345 43 | * 44 | * Deserialize the response into an object of type Account 45 | * 46 | * Check that the value of the Balance property of this account is equal to 98765 47 | */ 48 | [Test] 49 | public async Task GetAccount_CheckBalance_ShouldEqual98765() 50 | { 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Exercises05.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using NUnit.Framework; 4 | using RestSharp; 5 | using RestSharpWorkshop.Exercises.Models; 6 | using System.Threading.Tasks; 7 | 8 | namespace RestSharpWorkshop.Exercises 9 | { 10 | [TestFixture] 11 | public class Exercises05 12 | { 13 | // The base URL for our tests 14 | private const string BASE_URL = "http://localhost:9876"; 15 | 16 | // The RestSharp client we'll use to make our requests 17 | private RestClient client; 18 | 19 | [OneTimeSetUp] 20 | public void SetupRestSharpClient() 21 | { 22 | client = new RestClient(BASE_URL); 23 | } 24 | 25 | /******************************************************* 26 | * Create a new GraphQLQuery object and use the given 27 | * query as the value for the Query property 28 | * 29 | * POST this object to /simple-graphql 30 | * 31 | * Assert that the name of the CEO is Elon Musk 32 | * 33 | * Use "data.company.ceo" as the argument to SelectToken() 34 | * to extract the required value from the response 35 | */ 36 | [Test] 37 | public async Task GetCompanyData_checkCeo_shouldBeElonMusk() 38 | { 39 | string query = "{ company { name ceo } }"; 40 | } 41 | 42 | /******************************************************* 43 | * Create a data driven test with three TestCase iterations: 44 | * ------------------------------------ 45 | * rocket id | rocket name | country 46 | * ------------------------------------ 47 | * falcon1 | Falcon 1 | Republic of the Marshall Islands 48 | * falconheavy | Falcon Heavy | United States 49 | * starship | Starship | United States 50 | * 51 | * Parameterize the test 52 | * 53 | * Create a new GraphQL query from the given query string 54 | * Pass in the rocket id as a variable value (the variable name is 'id') 55 | * 56 | * POST this object to /graphql-with-variables 57 | * 58 | * Assert that the name of the rocket is equal to the value in the TestCase 59 | * Use "data.rocket.name" as the argument to SelectToken() 60 | * to extract the required value from the response 61 | * 62 | * Also, assert that the country of the rocket is equal to the value in the TestCase 63 | * Use "data.rocket.country" as the argument to SelectToken() 64 | * to extract the required value from the response 65 | */ 66 | [Test] 67 | public async Task getRocketDataById_checkNameAndCountry() 68 | { 69 | string query = @" 70 | query getRocketData($id: ID!) 71 | { 72 | rocket(id: $id) { 73 | name 74 | country 75 | } 76 | } 77 | "; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Models/Account.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Exercises.Models 4 | { 5 | public class Account 6 | { 7 | [JsonProperty("type")] 8 | public string Type { get; set; } 9 | [JsonProperty("balance")] 10 | public int Balance { get; set; } = 0; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RestSharpWorkshop/Exercises/Models/GraphQLQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace RestSharpWorkshop.Exercises.Models 4 | { 5 | public class GraphQLQuery 6 | { 7 | [JsonProperty("query")] 8 | public string Query { get; set; } 9 | [JsonProperty("variables")] 10 | public string Variables { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RestSharpWorkshop/RestSharpWorkshop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0;net8.0;net9.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /RestSharpWorkshop/TestBase.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using WireMock.Matchers; 3 | using WireMock.RequestBuilders; 4 | using WireMock.ResponseBuilders; 5 | using WireMock.Server; 6 | 7 | namespace RestSharpWorkshop 8 | { 9 | public class TestBase 10 | { 11 | protected WireMockServer Server { get; private set; } 12 | 13 | private readonly string parameterizedQuery = @"query getRocketData($id: ID!) 14 | { 15 | rocket(id: $id) { 16 | name 17 | country 18 | } 19 | }"; 20 | 21 | [SetUp] 22 | public void StartServer() 23 | { 24 | this.Server = WireMockServer.Start(9876); 25 | 26 | SetupMockAnswers(); 27 | } 28 | 29 | [TearDown] 30 | public void StopServer() 31 | { 32 | this.Server?.Stop(); 33 | } 34 | 35 | private void SetupMockAnswers() 36 | { 37 | AddMockResponseForCustomer12212(); 38 | AddMockResponseForCustomer12345(); 39 | AddMockResponseForCustomer12456(); 40 | AddMockResponseForBasicAuth(); 41 | AddMockResponseForOAuth2(); 42 | AddMockResponseForPostAccount(); 43 | AddMockResponseForGetAccount12345(); 44 | AddMockResponseForSimpleGraphQLQuery(); 45 | AddMockResponseForGraphQLQueryWithVariablesFalcon1(); 46 | AddMockResponseForGraphQLQueryWithVariablesFalconHeavy(); 47 | AddMockResponseForGraphQLQueryWithVariablesStarship(); 48 | } 49 | 50 | private void AddMockResponseForCustomer12212() 51 | { 52 | var customer = new 53 | { 54 | firstName = "John", 55 | lastName = "Smith", 56 | address = new 57 | { 58 | street = "Main Street 123", 59 | city = "Beverly Hills" 60 | } 61 | }; 62 | 63 | this.Server.Given(Request.Create().WithPath("/customer/12212").UsingGet()) 64 | .RespondWith(Response.Create() 65 | .WithHeader("Content-Type", "application/json") 66 | .WithHeader("Server", "MockServer") 67 | .WithHeader("MyHeader", "MyHeaderValue") 68 | .WithBodyAsJson(customer) 69 | .WithStatusCode(200)); 70 | } 71 | 72 | private void AddMockResponseForCustomer12345() 73 | { 74 | var customer = new 75 | { 76 | firstName = "Susan", 77 | lastName = "Jones", 78 | address = new 79 | { 80 | street = "Main Street 234", 81 | city = "Beverly Hills" 82 | } 83 | }; 84 | 85 | this.Server.Given(Request.Create().WithPath("/customer/12345").UsingGet()) 86 | .RespondWith(Response.Create() 87 | .WithHeader("Content-Type", "application/json") 88 | .WithBodyAsJson(customer) 89 | .WithStatusCode(200)); 90 | } 91 | 92 | private void AddMockResponseForCustomer12456() 93 | { 94 | var customer = new 95 | { 96 | firstName = "Anna", 97 | lastName = "Brown", 98 | address = new 99 | { 100 | street = "Main Street 456", 101 | city = "Beverly Hills" 102 | } 103 | }; 104 | 105 | this.Server.Given(Request.Create().WithPath("/customer/12456").UsingGet()) 106 | .RespondWith(Response.Create() 107 | .WithHeader("Content-Type", "application/json") 108 | .WithBodyAsJson(customer) 109 | .WithStatusCode(200)); 110 | } 111 | 112 | private void AddMockResponseForBasicAuth() 113 | { 114 | var response = new 115 | { 116 | token = "this_is_your_oauth2_token" 117 | }; 118 | 119 | this.Server.Given(Request.Create().WithPath("/token").UsingGet() 120 | .WithHeader("Authorization", new ExactMatcher("Basic am9objpkZW1v"))) 121 | .RespondWith(Response.Create() 122 | .WithHeader("Content-Type", "application/json") 123 | .WithBodyAsJson(response) 124 | .WithStatusCode(200)); 125 | } 126 | 127 | private void AddMockResponseForOAuth2() 128 | { 129 | this.Server?.Given(Request.Create().WithPath("/secure/customer/12212").UsingGet() 130 | .WithHeader("Authorization", new ExactMatcher("Bearer this_is_your_oauth2_token"))) 131 | .RespondWith(Response.Create() 132 | .WithStatusCode(200)); 133 | } 134 | 135 | private void AddMockResponseForPostAccount() 136 | { 137 | this.Server?.Given(Request.Create().WithPath("/customer/12212/accounts").UsingPost() 138 | .WithBody(new JsonMatcher("{\"type\": \"savings\", \"balance\": 0}"))) 139 | .RespondWith(Response.Create() 140 | .WithStatusCode(201)); 141 | } 142 | 143 | private void AddMockResponseForGetAccount12345() 144 | { 145 | var account = new 146 | { 147 | type = "savings", 148 | balance = 98765 149 | }; 150 | 151 | this.Server.Given(Request.Create().WithPath("/customer/12212/account/12345").UsingGet()) 152 | .RespondWith(Response.Create() 153 | .WithHeader("Content-Type", "application/json") 154 | .WithBodyAsJson(account) 155 | .WithStatusCode(200)); 156 | } 157 | 158 | /// 159 | /// Creates the stub response for the simple GraphQL example. 160 | /// 161 | private void AddMockResponseForSimpleGraphQLQuery() 162 | { 163 | var response = new 164 | { 165 | data = new 166 | { 167 | company = new 168 | { 169 | name = "SpaceX", 170 | ceo = "Elon Musk", 171 | }, 172 | }, 173 | }; 174 | 175 | this.Server?.Given(Request.Create().WithPath("/simple-graphql").UsingPost() 176 | .WithBody(new JmesPathMatcher("query == '{ company { name ceo } }'"))) 177 | .RespondWith(Response.Create() 178 | .WithStatusCode(200) 179 | .WithHeader("Content-Type", "application/json") 180 | .WithBodyAsJson(response)); 181 | } 182 | 183 | private void AddMockResponseForGraphQLQueryWithVariablesFalcon1() 184 | { 185 | var response = new 186 | { 187 | data = new 188 | { 189 | rocket = new 190 | { 191 | name = "Falcon 1", 192 | country = "Republic of the Marshall Islands", 193 | }, 194 | }, 195 | }; 196 | 197 | this.Server?.Given(Request.Create().WithPath("/graphql-with-variables").UsingPost() 198 | .WithBody(new JmesPathMatcher("contains(variables, 'falcon1')"))) 199 | .RespondWith(Response.Create() 200 | .WithStatusCode(200) 201 | .WithHeader("Content-Type", "application/json") 202 | .WithBodyAsJson(response)); 203 | } 204 | 205 | private void AddMockResponseForGraphQLQueryWithVariablesFalconHeavy() 206 | { 207 | var response = new 208 | { 209 | data = new 210 | { 211 | rocket = new 212 | { 213 | name = "Falcon Heavy", 214 | country = "United States", 215 | }, 216 | }, 217 | }; 218 | 219 | this.Server?.Given(Request.Create().WithPath("/graphql-with-variables").UsingPost() 220 | .WithBody(new JmesPathMatcher("contains(variables, 'falconheavy')"))) 221 | .RespondWith(Response.Create() 222 | .WithStatusCode(200) 223 | .WithHeader("Content-Type", "application/json") 224 | .WithBodyAsJson(response)); 225 | } 226 | 227 | private void AddMockResponseForGraphQLQueryWithVariablesStarship() 228 | { 229 | var response = new 230 | { 231 | data = new 232 | { 233 | rocket = new 234 | { 235 | name = "Starship", 236 | country = "United States", 237 | }, 238 | }, 239 | }; 240 | 241 | this.Server?.Given(Request.Create().WithPath("/graphql-with-variables").UsingPost() 242 | .WithBody(new JmesPathMatcher("contains(variables, 'starship')"))) 243 | .RespondWith(Response.Create() 244 | .WithStatusCode(200) 245 | .WithHeader("Content-Type", "application/json") 246 | .WithBodyAsJson(response)); 247 | } 248 | } 249 | } 250 | --------------------------------------------------------------------------------