how to set up an asp.net 5 continuous delivery pipeline using ibm bluemix devops services
TRANSCRIPT
© IBM Corporation 1
Presented by:
How to set up an ASP.NET 5 Continuous Delivery Pipeline using IBM Bluemix DevOps Services Richard Johansson
Linus KarlssonFredrik LindahlOlof Kindblad
DevOps Interns, Bluemix Garage, IBM Malmö
2016-03-16
© IBM Corporation 2
Introduction
This presentation is a result of an applied project internship carried out during the spring of 2016 by four Information Systems students from Lund University School of Economics and Management for IBM Bluemix Garage in Malmö, Sweden. The project’s purpose was to assess the current capabilities, opportunities and pain points in using the ASP.NET 5 runtime with Bluemix and the DevOps Services, and based on that provide recommendations to IBM on how they can enhance that experience and thus motivate more .NET developers to choose Bluemix, DevOps Services and the Bluemix Garage Method as their Continuous Delivery and DevOps enablers.
The result of the assessment showed that the Bluemix and .NET experience is not bad at all, although one pain point was that there is no built-in support for a .NET runtime environment in the DevOps Services Delivery Pipeline – which is not surprising since ASP.NET 5 is so far only tagged as an experimental runtime in Bluemix. A .NET supportive runtime environment is however possible to install manually, as the Delivery Pipeline offers its users to specify their own manual build scripts. How to do this is explained in this presentation. Altogether, we can conclude that Bluemix and the DevOps Services is most definitely a full-worthy option to other popular hybrid cloud solutions for .NET developers.
In this presentation we will explain how to set up a complete IBM native tool chain for Continuous Delivery by using the IBM DevOps Services Build & Deploy function, also known as the Delivery Pipeline. We will develop, build, (autonomously) unit test, integration test, load test and deploy an ASP.NET 5 application with a Cloudant NoSQL database to Bluemix.
© IBM Corporation 3
Orientation
This guide picks up and extends the Getting started with ASP.NET 5 in Bluemix guide, which shows how to create a simple HelloWorld ASP.NET 5 application and deploy it to Bluemix. That guide briefly mentions the ASP.NET 5 + Cloudant Starter App boilerplate, which can be used to generate a pre-built ASP.NET 5 application with basic CRUD functionality using a Cloudant NoSQL database. That is the boilerplate we used as a starting point for this guide. Our own application extends that one with automated unit tests, automated integration tests and the required project dependencies for building and testing in the DevOps Services Delivery Pipeline.
The Getting started with ASP.NET 5 in Bluemix guide:https://developer.ibm.com/bluemix/2015/05/18/getting-started-asp-net-5-bluemix
The ASP.NET 5 + Cloudant Starter App boilerplate that we started from is available here:https://github.com/IBM-Bluemix/asp.net5-cloudant
Our finished application that was created during the making of this guide is available here:https://github.com/FredrikLindahl/ASP.NET-5-Cloudant-for-Bluemix-Delivery-Pipeline
© IBM Corporation 4
The Delivery PipelineOur Delivery Pipeline was configured in four stages.
– The Build stage has two jobs: one Build job that builds the application and the runtime in the Delivery Pipeline server environment, and one Unit Test job that first builds and then executes some basic unit tests.
– If the first stage is passed, the Test stage is triggered. This stage has one job: an Integration Test job that runs some basic CRUD tests against the Cloudant database.
– If the second stage is passed, the Staging stage is triggered. This stage has two jobs: a Deployment job that deploys the build to a “pre-production” test environment in the Bluemix cloud, which is accessed through myApplication-PreProd.eu-gb.mybluemix.net. The stage also has a Load Test job that runs some performance tests using the third party tool Load Impact that is available as a Bluemix Service and API.
– If the third stage is passed, the Production stage is triggered. This stage only has one job: a Deployment job that deploys the build to a live production environment in the Bluemix cloud, accessible via myApplication.eu-gb.mybluemix.net.
The Toolchain
IDE: Visual Studio CodeSCM: DevOps Services JazzHubCI & CD: DevOps Services Delivery PipelineUnit Testing: xUnitIntegration Testing: xUnitLoad Testing: Load Impact
© IBM Corporation 5
text
Overview of the Delivery Pipeline Setup Process
© IBM Corporation 6© IBM Corporation 6
The GuideCreated and verified in March 2016
© IBM Corporation 7© IBM Corporation 7
Start Main ProcessDelivery Pipeline
© IBM Corporation 8© IBM Corporation 8
Start Main ProcessDelivery Pipeline
© IBM Corporation 9
Delivery Pipeline1. Create Application from Boilerplate
• Go to https://github.com/IBM-Bluemix/asp.net5-cloudant
• Click Deploy to Bluemix
• When finished, click EDIT CODE1. Create Application from Boilerplate
© IBM Corporation 10
Delivery Pipeline2. Develop Code
• Make some code changes, i.e. change the title of the page to “HelloWorldIBM”
3. Commit and Push Code
• Commit and push code changes to your JazzHub git repository.
4. Trigger Delivery Pipeline
This step is done automatically each time a code change is pushed to the JazzHub git repository.
2. Develop Code3. Commit and Push Code4. Trigger Delivery Pipeline
© IBM Corporation 11
Delivery Pipeline5. Add Build Stage
This one-time-step is automatically done by the Delivery Pipeline as part of setting up the structure.
6. Add Build Job
This one-time-step is automatically done by the Delivery Pipeline as part of setting up the structure.
7. Add Unit Test Job
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
•Click on Configure Stage in the Build Stage•Go to the tab Jobs•Add a new Test job and name it "Unit Test"•Select Tester Type: Simple•Make sure that the Unit Test Job is sequentially positioned after the
Build Job in the Build Stage
8. Deploy to Build Stage
This continuous-delivery-step is automatically done each time the Delivery Pipeline is triggered.
5. Add Build Stage6. Add Build Job7. Add Unit Test Job8. Deploy to Build Stage
© IBM Corporation 12© IBM Corporation 12
Start Sub-ProcessDelivery Pipeline: Build
© IBM Corporation 13© IBM Corporation 13
Start Sub-ProcessDelivery Pipeline: Build
© IBM Corporation 14
Overview Sub-ProcessDelivery Pipeline: Build
© IBM Corporation 15
Delivery Pipeline: Build9. Configure Builder Type to Shell Script10. Add ASP.NET 5 Manual Build Script
9. Configure Builder Type to Shell Script• In the Delivery Pipeline, select Configure on the Build Stage
• Go to the tab Jobs• Set Builder Type to Shell Script
10. Add ASP.NET Manual Build Script• Insert the manual build script below. Further explanation of what the script does is
available in appendix A1.
• Make sure to replace the project path with your own in cd src/your-project-path
#!/bin/bashecho --- UPDATING DEPENDENCIES! ---sudo apt-get update
echo --- DOWNLOADING PACKAGES! ---sudo apt-get -y install libunwind8 gettext libssl-dev libcurl3-dev zlib1g libcurl4-openssl-dev libicu-dev uuid-dev
echo --- DOWNLOADING DNVM! ---curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh
echo --- INSTALLING DNVM! ---dnvm install 1.0.0-rc1-final -r coreclr -a x64
echo --- EXECUTING RESTORE! ---cd src/your-project-pathdnu restore
echo --- EXECUTING BUILD! ---dnu build
© IBM Corporation 16
Delivery Pipeline: Build11. Run Build Script12. Upload Artifact
11. Run Build Script
This step is automated when triggering the Delivery Pipeline. To manually run it, click Run Stage on the Build Stage.
12. Upload Artifact
The Build Job in the Build Stage is automatically run and the build artifact is automatically uploaded.
© IBM Corporation 17© IBM Corporation 17
End Sub-ProcessDelivery Pipeline: Build
© IBM Corporation 18© IBM Corporation 18
Start Sub-ProcessDelivery Pipeline: Unit Test
© IBM Corporation 19© IBM Corporation 19
Start Sub-ProcessDelivery Pipeline: Unit Test
© IBM Corporation 20
Overview Sub-ProcessDelivery Pipeline: Unit Test
© IBM Corporation 21
Delivery Pipeline: Unit Test13. Add xUnit Dependencies to project.json
13. Add xUnit Dependencies to project.json• Navigate to the project.json file of your application and add the following dependencies:
"xunit": "2.1.0""xunit.runner.dnx": "2.1.0-rc1-build204“
The project.json file should look something like this:
{ "version": "1.0.0-*", "dependencies": { "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final", "Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final", "Microsoft.AspNet.Mvc": "6.0.0-rc1-final", "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final", "Newtonsoft.Json": "7.0.1", "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final", "Microsoft.AspNet.WebApi.Client": "5.2.3", "System.Net.Http": "4.0.0", "System.Runtime.Serialization.Xml": "4.0.10", "xunit": "2.1.0", "xunit.runner.dnx": "2.1.0-rc1-build204" }, "commands": { "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5004" }, "frameworks": { "dnxcore50": { } }, "exclude": [ "wwwroot", "node_modules" ], "publishExclude": [ "**.user", "**.vspscc" ]}
© IBM Corporation 22
Delivery Pipeline: Unit Test14. Add xUnit Command to project.json
14. Add xUnit Command to project.json• Navigate to the project.json file of your application and add the following command:
test": "xunit.runner.dnx"
The project.json file should look something like this:
{ "version": "1.0.0-*", "dependencies": { "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final", "Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final", "Microsoft.AspNet.Mvc": "6.0.0-rc1-final", "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final", "Newtonsoft.Json": "7.0.1", "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final", "Microsoft.AspNet.WebApi.Client": "5.2.3", "System.Net.Http": "4.0.0", "System.Runtime.Serialization.Xml": "4.0.10", "xunit": "2.1.0", "xunit.runner.dnx": "2.1.0-rc1-build204" }, "commands": { "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5004","test": "xunit.runner.dnx" }, "frameworks": { "dnxcore50": { } }, "exclude": [ "wwwroot", "node_modules" ], "publishExclude": [ "**.user", "**.vspscc" ]}
© IBM Corporation 23
Delivery Pipeline: Unit Test15. Create Unit Test Class16. Write Unit Test(s)
15. Create Unit Test Class• In Visual Studio, navigate to your project folder
• Add a new folder in src called "Tests"
• Create a new file in the Tests folder called "UnitTests.cs“
16. Write Unit Test(s)• Open the UnitTests class and write some unit tests.
• Make sure to import the Xunit package at the top of the class (using Xunit;) and to specify the
correct namespace/folder that the class lies in (namespace your-project-path.Tests).
• An example of a class that has a few basic unit tests can be found in appendix A2.
© IBM Corporation 24
Delivery Pipeline: Unit Test17. Insert ASP.NET 5 Manual Build Script18. Remove DNU Build From Script19. Insert Unit Test Call Script20. Run Unit Tests
17. Insert ASP.NET 5 Manual Build Script• In the Delivery Pipeline, navigate to the Unit Test Job in the Build Stage• Insert the manual build script in the Test Command console
18. Remove DNU Build From Script• Delete the following lines from the manual build Shell Script:
echo --- EXECUTING BUILD! ---dnu build
19. Insert Unit Test Call Script• Call the xUnit unit test(s) by adding the below command at the end of the Shell Script:
dnx test -class path-to-unittest-class
• Example:
dnx test -class dotnetCloudantWebstarter.Tests.UnitTests
20. Run Unit Tests• This step is automatically run when committing code changes and thus triggering the Build
Stage and its Build Job and Unit Test Job.
• (For manual triggering, click Run Stage in the Build Stage).
© IBM Corporation 25© IBM Corporation 25
End Sub-ProcessDelivery Pipeline: Unit Test
© IBM Corporation 26© IBM Corporation 26
Continue Main ProcessDelivery Pipeline
© IBM Corporation 27© IBM Corporation 27
Continue Main ProcessDelivery Pipeline
© IBM Corporation 28
Delivery Pipeline21. Add Test Stage
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
• In the Delivery Pipeline, select ADD STAGE• Name the stage "Test"• Input Type: Build Artifacts• Stage: Build• Job: Build• State Trigger: Run jobs when the previous stage is completed• Make sure that the Test Stage is positioned after the Build Stage in the
Delivery Pipeline
22. Add Integration Test Job
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
• Click on Configure Stage in the Test Stage• Go to the tab Jobs• Add a new Test job and name it "Integration Test"• Select Tester Type: Simple
23. Deploy to Test Stage
This continuous-delivery-step can be set to be triggered either manually or automatically, by changing the State Trigger settings in the Test Stage.
21. Add Test Stage22. Add Integration Test Job23. Deploy to Test Stage
© IBM Corporation 29© IBM Corporation 29
Start Sub-ProcessDelivery Pipeline: Integration Test
© IBM Corporation 30© IBM Corporation 30
Start Sub-ProcessDelivery Pipeline: Integration Test
© IBM Corporation 31
Overview Sub-ProcessDelivery Pipeline: Integration Test
© IBM Corporation 32
Delivery Pipeline: Integration Test24. Add xUnit Dependencies to project.json25. Add xUnit Command to project.json
24. Add xUnit Dependencies to project.json
Note: This step has already been described as part of the Unit Testing setup manual. It can thus be omitted if the xUnit dependencies and command is already configured.
25. Add xUnit Command to project.json
Note: This step has already been described as part of the Unit Testing setup manual. It can thus be omitted if the xUnit dependencies and command is already configured.
© IBM Corporation 33
Delivery Pipeline: Integration Test26. Create Integration Test Class27. Write Integration Test(s)
26. Create Integration Test Class• In Visual Studio, navigate to your project folder
• If not done already, add a new folder in src called "Tests"
• Create a new file in the Tests folder called “IntegrationTests.cs“
27. Write Integration Test(s)• Open the IntegrationTests class and write some integration tests.
• Make sure to import the Xunit package at the top of the class (using Xunit;) and to specify the
correct namespace/folder that the class lies in (namespace your-project-path.Tests).
• An example of a class that has a few basic integration tests can be found in appendix A3.
© IBM Corporation 34
Delivery Pipeline: Integration Test28. Add VCAP_SERVICES
28. Add VCAP_SERVICES
This step establishes the connection between the database and the Delivery Pipeline.
• Retrieve VCAP_SERVICES from the Cloudant DB Service
• Navigate to the Application Overview via the Bluemix Dashboard
• Select the Show Credentials arrow button from the Cloudant DB Service
• Copy the VCAP_SERVICES credentials.
• Add VCAP_SERVICES to the Delivery Pipeline Test Job
• Click on Configure Stage in the Test Stage• Go to the tab ENVIRONMENT PROPERTIES• Add a new Text Area Property
•Name: VCAP_SERVICES
•Value: Paste the previously copied VCAP_SERVICES credentials
© IBM Corporation 35
Delivery Pipeline: Integration Test29. Insert ASP.NET 5 Manual Build Script30. Remove DNU Build From Script31. Insert Integration Test Call Script32. Run Integration Tests
29. Insert ASP.NET 5 Manual Build Script• In the Delivery Pipeline, navigate to the Integration Test Job in the Test Stage• Insert the manual build script in the Test Command console
30. Remove DNU Build From Script• Delete the following lines from the manual build Shell Script:
echo --- EXECUTING BUILD! ---dnu build
31. Insert Integration Test Call Script• Call the xUnit integration test(s) by adding the below command at the end of the Shell Script:
dnx test -class path-to-integrationtest-class
• Example:
dnx test -class dotnetCloudantWebstarter.Tests.IntegrationTests
32. Run Integration Tests• This stage can be set to be triggered manually or automatically by changing the State Trigger
settings in the Test Stage. If automatic, this step is automatically triggered when the Build Stage is passed.
• (For manual triggering, click Run Stage in the Test Stage).
© IBM Corporation 36© IBM Corporation 36
End Sub-ProcessDelivery Pipeline: Integration Test
© IBM Corporation 37© IBM Corporation 37
Continue Main ProcessDelivery Pipeline
© IBM Corporation 38© IBM Corporation 38
Continue Main ProcessDelivery Pipeline
© IBM Corporation 39
Delivery Pipeline33. Add Staging Stage
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
•In the Delivery Pipeline, select ADD STAGE•Name the stage "Staging"
•Input Type: Build Artifacts
•Stage: Build
•Job: Build
•State Trigger: Run jobs when the previous stage is completed
•Make sure that the Staging Stage is positioned after the Test Stage in the Delivery Pipeline
34. Add Pre-Production Deployment Job
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
•Click on Configure Stage in the Staging Stage•Go to the tab Jobs•Add a new Deploy job and name it "Deploy to Pre-Prod“•In the Deploy Script command window, add the following script in order to create and route to a new
application which will act as our pre-production environment:
cf push "${CF_APP}"-PreProd -n "${CF_APP}"-PreProd
33. Add Staging Stage34. Add Pre-Production Deployment Job
© IBM Corporation 40
Delivery Pipeline35. Add Load Impact Test Job
This one-time step needs to be manually done as part of setting up the structure of the Delivery Pipeline.
•In the Delivery Pipeline, click Configure Stage in the Staging Stage•Go to the tab Jobs•Add a new Test job and name it "Load Test"•Select Tester Type: Simple•Include the following command (as-is) in the Test Command console:
curl -X POST "${LOAD_IMPACT_TEST_URL}" -u "${LOAD_IMPACT_API_TOKEN}:“
36. Deploy to Staging Stage
This continuous-delivery-step can be set to be triggered either manually or automatically, by changing the State Trigger settings in the Staging Stage.
35. Add Load Impact Test Job36. Deploy to Staging Stage
© IBM Corporation 41© IBM Corporation 41
Start Sub-ProcessDelivery Pipeline: Load Test
© IBM Corporation 42© IBM Corporation 42
Start Sub-ProcessDelivery Pipeline: Load Test
© IBM Corporation 43
Overview Sub-ProcessDelivery Pipeline: Load Test
© IBM Corporation 44
Delivery Pipeline: Load Test37. Add Load Impact as a Service38. Open Load Impact Dashboard39. Retrieve API Token
37. Add Load Impact as a Service• In Bluemix, select the space to add the Load Impact service to• Click ADD SERVICE OR API• Find and click the Load Impact service• Select the appropriate plan and name your service• Click CREATE
This step can also be done using the Cloud Foundry CLI command:
$ cf create-service loadimpact lifree my-loadimpact-service
38. Open Load Impact Dashboard• Navigate to the created Load Impact Service in Bluemix• Click OPEN LOAD IMPACT DASHBOARD• The Load Impact website is opened
39. Retrieve API Token• On the Load Impact website, navigate to Integrations Menu• Choose Load Impact API Token• Click Generate token and then copy and save the API token that is generated
© IBM Corporation 45
Delivery Pipeline: Load Test40. Create Load Test41. Run Load Test42. Retrieve Load Impact Test ID
40. Create Load Test• On the Load Impact website, navigate to Tests Menu• Click Add user scenario• Paste the Bluemix application's Pre-Production URL and click Add• Set desirable VUs and duration• Click Save changes
41. Run Load Test• On the Load Impact website, in the Tests Menu, click Run test to create and run a simple load test
42. Retrieve Load Impact Test ID• On the Load Impact website, on the Tests page, click the test that you just ran• Select and copy the ID at the end of URL from your browser and save it
For example, if your test case URL is https://app.loadimpact.com/tests/1234567, the config ID is 1234567. This ID, along with the API token, will be used when configuring your app to use Load Impact.
© IBM Corporation 46
Delivery Pipeline: Load Test43. Set Environment Properties44. Run Load Test in Delivery Pipeline
43. Set Environment PropertiesThis step establishes the link between the Load Impact test and the Delivery Pipeline.
• In the Delivery Pipeline's Staging Stage, click the Environment Properties tab
• Click Add Property• Choose Text Property from the list and name it "LOAD_IMPACT_API_TOKEN"
• For the value of this property, enter the Load Impact API key that you generated earlier
• Click Add Property• Add another Text Property and name it "LOAD_IMPACT_TEST_URL"• For the value of this property, enter the following URL, replacing id with the config ID of the test case
your created earlier: https://api.loadimpact.com/v2/test-configs/id/start
44. Run Load Test in Delivery Pipeline• This continuous-delivery-step can be set to be triggered either manually or automatically, by
changing the State Trigger settings in the Staging Stage. For manual triggering, click Run Stage in the Staging Stage.
• The test progress and results can be observed on the Load Impact website.
© IBM Corporation 47© IBM Corporation 47
End Sub-ProcessDelivery Pipeline: Load Test
© IBM Corporation 48© IBM Corporation 48
Continue Main ProcessDelivery Pipeline
© IBM Corporation 49© IBM Corporation 49
Continue Main ProcessDelivery Pipeline
© IBM Corporation 50
Delivery Pipeline45. Deploy to Production
45. Deploy to Production
This continuous-delivery-step can be set to be triggered either manually or automatically, by changing the State Trigger settings in the Staging Stage.
© IBM Corporation 51© IBM Corporation 51
End Main ProcessDelivery Pipeline
© IBM Corporation 52© IBM Corporation 52
Appendix
© IBM Corporation 53
A1: Manual Build Script
• Update Package Lists
Updates the package lists to make sure that the latest packages are downloaded.
echo --- UPDATING DEPENDENCIES! ---
sudo apt-get update
• Install Necessary Packages
Installs the below listed necessary runtime environment packages.• libunwind8 • gettext • libssl-dev• libcurl3-dev• zlib1g• libcurl4-openssl-dev• libicu-dev • uuid-dev
echo --- DOWNLOADING PACKAGES! ---
sudo apt-get -y install libunwind8 gettext libssl-dev libcurl3-dev zlib1g libcurl4-openssl-dev libicu-dev uuid-dev
• Download DNVM
Downloads DNVM (.NET Version Manager) so that we can restore and build and publish. DNX (.NET Execution Manager) and DNU (.NET Utilites) are included in DNVM.
echo --- DOWNLOADING DNVM! ---
curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh
• Install DNVM
Installs DNVM (.NET Version Manager).
Note: The DNVM version must correspond to the required DNVM version listed in the application's project.json file.
echo --- INSTALLING DNVM! ---
dnvm install 1.0.0-rc1-final -r coreclr -a x64
• Change Directory to Project Folder
Changes the directory to the project source path where the DNU commands are executed.
Note: Make sure to replace your-project-path with your own project path.
cd src/your-project-path
• Restore DNU Dependencies
Restores the project dependencies.
echo --- EXECUTING RESTORE! ---
dnu restore
• Build Project with DNU
Builds the artifact.
echo --- EXECUTING BUILD! ---
dnu build
© IBM Corporation 54
A2: Example Unit Test Classusing dotnetCloudantWebstarter.Models;using CloudantDotNet.Controllers;using Xunit;
namespace dotnetCloudantWebstarter.Tests { public class UnitTests { // Verifies ToDoItem constructor! [Fact] public void ToDoItem_ConstructorTest() { ToDoItem item = new ToDoItem(); Assert.NotNull(item); Assert.Null(item.id); Assert.Null(item.rev); Assert.Null(item.text); } // Verifies IntegrationTests constructor!
// NB! IntegrationTests_ConstructorTest()can only be run if an integration test class is setup accordingly! If no such class exists, disable the test. [Fact] public void IntegrationTests_ConstructorTest() { IntegrationTests integrationTest = new IntegrationTests(); Assert.NotNull(integrationTest.cloudantCreds); Assert.NotNull(integrationTest.createItem); Assert.NotNull(integrationTest.updateItem); Assert.NotNull(integrationTest.deleteItem); } }}
© IBM Corporation 55
A3-1: Example Integration Test Classusing System;using System.Threading.Tasks;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using Microsoft.AspNet.Mvc;using Newtonsoft.Json;using Microsoft.Extensions.OptionsModel;using dotnetCloudantWebstarter.Models;using CloudantDotNet.Controllers;using Xunit;using Microsoft.AspNet.Builder;using Microsoft.AspNet.Hosting;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;using Microsoft.Extensions.Configuration;using Newtonsoft.Json.Linq;
namespace dotnetCloudantWebstarter.Tests {
public class IntegrationTests {
private static readonly string dbName = "todos"; public Creds cloudantCreds;
public ToDoItem createItem;public ToDoItem updateItem;public ToDoItem deleteItem;public string createItemResponseID;
public string createItemResponseRev; public string updateItemResponseID; public string updateItemResponseRev; public string deleteItemResponseID; public string deleteItemResponseRev;
public bool createTestResult;public bool updateTestResult;public bool deleteTestResult;
// -----------------------------------------
© IBM Corporation 56
A3-2: Example Integration Test Class (Continued)public IntegrationTests() {
cloudantCreds = new Creds();string vcapServices = System.Environment.GetEnvironmentVariable("VCAP_SERVICES");
if (vcapServices != null) { dynamic json = JsonConvert.DeserializeObject(vcapServices); foreach (dynamic obj in json.Children()) { if (((string)obj.Name).ToLowerInvariant().Contains("cloudant")) { dynamic credentials = (((JProperty)obj).Value[0] as dynamic).credentials; if (credentials != null) { cloudantCreds.host = credentials.host; cloudantCreds.username = credentials.username; cloudantCreds.password = credentials.password;
break; } } } } createItem = new ToDoItem(); updateItem = new ToDoItem(); deleteItem = new ToDoItem(); }
// ------------------------------------------------
© IBM Corporation 57
A3-3: Example Integration Test Class (Continued)[Fact]public async void testCreate() {
createTestResult = await create(JsonConvert.DeserializeObject<ToDoItem>("{ 'text': 'Sample 1' }"), "testCreate");Assert.True(createTestResult);
}
[Fact]public async void testUpdate() {
await create(JsonConvert.DeserializeObject<ToDoItem>("{ 'text': 'Sample 2' }"), "testUpdate");updateItem.id = updateItemResponseID;updateItem.rev = updateItemResponseRev;updateItem.text = "TestUpdate";updateTestResult = await update(updateItem, "testUpdate");Assert.True(updateTestResult);
}
[Fact]public async void testDelete() {
await create(JsonConvert.DeserializeObject<ToDoItem>("{ 'text': 'Sample 3' }"), "testDelete");deleteItem.id = deleteItemResponseID;deleteItem.rev = deleteItemResponseRev;deleteTestResult = await delete(deleteItem, "testDelete");Assert.True(deleteTestResult);
}
// ------------------------------------------------
© IBM Corporation 58
A3-4: Example Integration Test Class (Continued)[HttpPost]public async Task<bool> create(ToDoItem item, string invoker) {
using (var client = cloudantClient()) {var response = await client.PostAsJsonAsync(dbName, item);
if (response.IsSuccessStatusCode) {var responseJson = await response.Content.ReadAsAsync<ToDoItem>();
if(invoker.Equals("testCreate")) {
this.createItemResponseID = responseJson.id;this.createItemResponseRev = responseJson.rev;
} else if(invoker.Equals("testUpdate")) {this.updateItemResponseID = responseJson.id;
this.updateItemResponseRev = responseJson.rev;
} else if(invoker.Equals("testDelete")) {this.deleteItemResponseID = responseJson.id;this.deleteItemResponseRev = responseJson.rev;
}
return true;}
string msg = "Failure to POST. Invoked by: " + invoker + ". Status Code: " + response.StatusCode + ". Reason: " + response.ReasonPhrase;
Console.WriteLine(msg); return false;
}} // ------------------------------------------------
© IBM Corporation 59
A3-5: Example Integration Test Class (Continued)[HttpPut]public async Task<bool> update(ToDoItem item, string invoker) { using (var client = cloudantClient()) {
var response = await client.PutAsJsonAsync(dbName + "/" + item.id + "?rev=" + item.rev, item);if (response.IsSuccessStatusCode) {
var responseJson = await response.Content.ReadAsAsync<ToDoItem>();Console.WriteLine("ITEM ID: " + responseJson.id + "ITEM REV: " + responseJson.rev);return true;
}string msg = "Failure to PUT. Invoked by: " + invoker + ". Status Code: " + response.StatusCode + ". Reason: " + response.ReasonPhrase;
Console.WriteLine(msg);return false;
}} // --------------------------------------------------
[HttpDelete]public async Task<bool> delete(ToDoItem item, string invoker) {
using (var client = cloudantClient()) {var response = await client.DeleteAsync(dbName + "/" + item.id + "?rev=" + item.rev);
if (response.IsSuccessStatusCode) {
return true;}
string msg = "Failure to POST. Invoked by: " + invoker + ". Status Code: " + response.StatusCode + ". Reason: " + response.ReasonPhrase;Console.WriteLine(msg);return false;
}}
// ------------------------------------------------
© IBM Corporation 60
A3-5: Example Integration Test Class (Continued)private HttpClient cloudantClient() {
if (cloudantCreds.username == null || cloudantCreds.password == null || cloudantCreds.host == null) {throw new Exception("Missing Cloudant NoSQL DB service credentials");
} var auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(cloudantCreds.username + ":" + cloudantCreds.password)); HttpClient client = HttpClientFactory.Create(new LoggingHandler()); client.BaseAddress = new Uri("https://" + cloudantCreds.host); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth); return client; }
} // END IntegrationTests CLASS!
// ------------------------------------------------
public class Creds {
public string username { get; set; } public string password { get; set; } public string host { get; set; }
}
class LoggingHandler : DelegatingHandler {protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken){
Console.WriteLine("{0}\t{1}", request.Method, request.RequestUri); var response = await base.SendAsync(request, cancellationToken); Console.WriteLine(response.StatusCode); return response;
}}}
© IBM Corporation 61
Presented by:
How to set up an ASP.NET 5 Continuous Delivery Pipeline using IBM Bluemix DevOps Services
Richard JohanssonLinus KarlssonFredrik LindahlOlof Kindblad
DevOps Interns, Bluemix Garage, IBM Malmö
2016-03-16
The End