WCF Webプログラミングモデルの学習テスト Part2

次はDataContractを使用してオブジェクトの POST / GET / PUT / DELETE を行うサービスの学習テスト。長いよ(w

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LearningWCFTests
{
    [TestClass]
    public class DataContractTest
    {
        ...

        [ServiceContract]
        public interface ICustomers
        {
            [OperationContract]
            [WebGet(UriTemplate = "customers")]
            List<Customer> GetCustomers();

            [OperationContract]
            [WebGet(UriTemplate = "customers/{id}")]
            Customer GetCustomer(string id);

            [OperationContract]
            [WebInvoke(Method = "POST", UriTemplate = "customers")]
            Customer AddCustomer(Customer customer);

            [OperationContract]
            [WebInvoke(Method = "PUT", UriTemplate = "customers/{id}")]
            Customer UpdateCustomer(string id, Customer customer);

            [OperationContract]
            [WebInvoke(Method = "DELETE", UriTemplate = "customers/{id}")]
            void DeleteCustomer(string id);
        }

        [DataContract]
        public class Customer
        {
            [DataMember]
            public string Id { get; set; }

            [DataMember]
            public string Name { get; set; }

            [DataMember]
            public string Address { get; set; }

            [DataMember]
            public Uri Uri { get; set; }

            public override string ToString()
            {
                return String.Format("{0}, {1}, {2}, {3}", Id, Name, Address, Uri);
            }
        }

        [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
        public class Customers : ICustomers
        {
            private int lastId_ = 0;
            private Dictionary<string, Customer> customers_ = new Dictionary<string, Customer>();
            private static object syncRoot_ = new Object();

            public List<Customer> GetCustomers()
            {
                List<Customer> customerList = new List<Customer>();

                lock (syncRoot_)
                {
                    foreach (Customer customer in this.customers_.Values)
                    {
                        customerList.Add(customer);
                    }
                }

                return customerList;
            }

            public Customer GetCustomer(string id)
            {
                if (!customers_.ContainsKey(id))
                {
                    // HTTP ステータス 404 
                    WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
                    return null;
                }

                Customer customer = customers_[id];

                return customer;
            }

            public Customer AddCustomer(Customer customer)
            {
                lock (syncRoot_)
                {
                    lastId_++;
                    customer.Id = lastId_.ToString();
                    UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;
                    UriTemplate template = new UriTemplate("customers/{id}");
                    customer.Uri = template.BindByPosition(match.BaseUri, customer.Id);
                    customers_.Add(customer.Id, customer);

                    // HTTP ステータス 201 
                    WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(customer.Uri);
                }

                return customer;
            }

            public Customer UpdateCustomer(string id, Customer customer)
            {
                if (!customers_.ContainsKey(id))
                {
                    // HTTP ステータス 404 
                    WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
                    return null;
                }

                lock (syncRoot_)
                {
                    customers_[id] = customer;
                }

                return customer;
            }

            public void DeleteCustomer(string id)
            {
                if (!customers_.ContainsKey(id))
                {
                    // HTTP ステータス 404 
                    WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
                    return;
                }

                lock (syncRoot_)
                {
                    customers_.Remove(id);
                }
            }
        }

        private static readonly Uri HostingBaseUri = new Uri("http://localhost:8000");
        private static readonly Uri ClientBaseUri = new Uri("http://localhost:8000");
        private WebServiceHost host_;
        private string aliceId_;
        private string bobId_;

        [TestInitialize]
        public void サービスホスティングの開始とCustomerの登録()
        {
            host_ = new WebServiceHost(typeof(Customers), HostingBaseUri);
            host_.Open();

            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();
                aliceId_ = channel.AddCustomer(new Customer() { Name = "Alice", Address = "123 Pike Place" }).Id;
                bobId_ = channel.AddCustomer(new Customer() { Name = "Bob", Address = "12323 Lake Shore Drive" }).Id;
            }
        }

        [TestCleanup]
        public void サービスホスティングの終了()
        {
            if (host_.State != CommunicationState.Closed)
                host_.Close();
        }

        [TestMethod]
        public void Customerが追加できるべき()
        {
            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();
                Customer john = channel.AddCustomer(new Customer() { Name = "John", Address = "4545 Wacker Drive" });

                string expectedJohn = String.Format("{0}, John, 4545 Wacker Drive, http://localhost:8000/customers/{0}", int.Parse(bobId_) + 1);
                Assert.AreEqual<string>(expectedJohn, john.ToString());
            }
        }

        [TestMethod]
        public void 生HTTPのPOSTでCustomerの追加ができるべき()
        {
            UriTemplate template = new UriTemplate("customers");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri));
            request.Method = "POST";
            request.ContentType = "application/xml; charset=utf-8";

            string requestBody = 
                "<DataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                    "<Address>4545 Wacker Drive</Address>" +
                    "<Id i:nil=\"true\"/>" +
                    "<Name>John</Name>" +
                    "<Uri i:nil=\"true\"/>" +
                "</DataContractTest.Customer>";

            byte[] bytes = Encoding.UTF8.GetBytes(requestBody);
            request.ContentLength = bytes.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(bytes, 0, bytes.Length);
            }

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Assert.AreEqual<HttpStatusCode>(HttpStatusCode.Created, response.StatusCode);

            string responseBody = string.Format(
                "<DataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                    "<Address>4545 Wacker Drive</Address>" +
                    "<Id>{0}</Id>" +
                    "<Name>John</Name>" +
                    "<Uri>http://localhost:8000/customers/{0}</Uri>" +
                "</DataContractTest.Customer>", int.Parse(bobId_) + 1);

            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                Assert.AreEqual<string>(responseBody, reader.ReadToEnd());
            }
        }

        [TestMethod]
        public void Customerの一覧が取得できるべき()
        {
            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();
                List<Customer> culstomerList = channel.GetCustomers();

                Assert.AreEqual<int>(2, culstomerList.Count);
            }
        }

        [TestMethod]
        public void 生HTTPのGETでCustomerの一覧が取得できるべき()
        {
            UriTemplate template = new UriTemplate("customers");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri));
            request.Method = "GET";
            request.ContentType = "application/xml; charset=utf-8";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Assert.AreEqual<HttpStatusCode>(HttpStatusCode.OK, response.StatusCode);

            string responseBody = string.Format(
                "<ArrayOfDataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                    "<DataContractTest.Customer>" +
                        "<Address>123 Pike Place</Address>" +
                        "<Id>{0}</Id>" +
                        "<Name>Alice</Name>" +
                        "<Uri>http://localhost:8000/customers/{0}</Uri>" +
                    "</DataContractTest.Customer>" +
                    "<DataContractTest.Customer>" +
                        "<Address>12323 Lake Shore Drive</Address>" +
                            "<Id>{1}</Id>" +
                            "<Name>Bob</Name>" +
                            "<Uri>http://localhost:8000/customers/{1}</Uri>" +
                    "</DataContractTest.Customer>" +
                "</ArrayOfDataContractTest.Customer>", aliceId_, bobId_);

            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                Assert.AreEqual<string>(responseBody, reader.ReadToEnd());
            }
        }

        [TestMethod]
        public void Customerが取得できるべき()
        {
            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();

                Customer alice = channel.GetCustomer(aliceId_);
                string expectedAlice = String.Format("{0}, Alice, 123 Pike Place, http://localhost:8000/customers/{0}", aliceId_);
                Assert.AreEqual<string>(expectedAlice, alice.ToString());

                Customer bob = channel.GetCustomer(bobId_);
                string expectedBob = String.Format("{0}, Bob, 12323 Lake Shore Drive, http://localhost:8000/customers/{0}", bobId_);
                Assert.AreEqual<string>(expectedBob, bob.ToString());
            }
        }

        [TestMethod]
        public void 生HTTPのGETでCustomerが取得できるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, aliceId_));
            request.Method = "GET";
            request.ContentType = "application/xml; charset=utf-8";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Assert.AreEqual<HttpStatusCode>(HttpStatusCode.OK, response.StatusCode);

            string responseBody = string.Format(
                "<DataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                    "<Address>123 Pike Place</Address>" +
                    "<Id>{0}</Id>" +
                    "<Name>Alice</Name>" +
                    "<Uri>http://localhost:8000/customers/{0}</Uri>" +
                "</DataContractTest.Customer>", aliceId_);

            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                Assert.AreEqual<string>(responseBody, reader.ReadToEnd());
            }
        }

        [TestMethod]
        public void Customerが存在しない場合の取得はHTTPステータス404が返されるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, "9"));
            request.Method = "GET";
            request.ContentType = "application/xml; charset=utf-8";

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.Fail();
            }
            catch (WebException ex)
            {
                Assert.AreEqual<WebExceptionStatus>(WebExceptionStatus.ProtocolError, ex.Status);
                Assert.AreEqual<HttpStatusCode>(HttpStatusCode.NotFound, ((HttpWebResponse)ex.Response).StatusCode);
            }
        }

        [TestMethod]
        public void Customerが更新できるべき()
        {
            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();

                Customer alice = channel.GetCustomer(aliceId_);
                alice.Name = "Charlie";
                Customer charlie = channel.UpdateCustomer(aliceId_, alice);

                Assert.AreEqual<string>("Charlie", charlie.Name);
            }
        }

        [TestMethod]
        public void 生HTTPのPUTでCustomerの更新ができるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, aliceId_));
            request.Method = "PUT";
            request.ContentType = "application/xml; charset=utf-8";

            string requestBody = string.Format(
                "<DataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                   "<Address>123 Pike Place</Address>" +
                   "<Id>{0}</Id>" +
                   "<Name>Charlie</Name>" +
                   "<Uri>http://localhost:8000/customers/{0}</Uri>" +
               "</DataContractTest.Customer>", aliceId_);

            byte[] bytes = Encoding.UTF8.GetBytes(requestBody);
            request.ContentLength = bytes.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(bytes, 0, bytes.Length);
            }

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Assert.AreEqual<HttpStatusCode>(HttpStatusCode.OK, response.StatusCode);

            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                Assert.AreEqual<string>(requestBody, reader.ReadToEnd());
            }
        }

        [TestMethod]
        public void Customerが存在しない場合の更新はHTTPステータス404が返されるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, "9"));
            request.Method = "PUT";
            request.ContentType = "application/xml; charset=utf-8";

            string requestBody =
                "<DataContractTest.Customer xmlns=\"http://schemas.datacontract.org/2004/07/LearningWCFTests\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
                   "<Address>NoWhere</Address>" +
                   "<Id>9</Id>" +
                   "<Name>Che</Name>" +
                   "<Uri>http://localhost:8000/customers/9</Uri>" +
               "</DataContractTest.Customer>";

            byte[] bytes = Encoding.UTF8.GetBytes(requestBody);
            request.ContentLength = bytes.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                requestStream.Write(bytes, 0, bytes.Length);
            }

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.Fail();
            }
            catch (WebException ex)
            {
                Assert.AreEqual<WebExceptionStatus>(WebExceptionStatus.ProtocolError, ex.Status);
                Assert.AreEqual<HttpStatusCode>(HttpStatusCode.NotFound, ((HttpWebResponse)ex.Response).StatusCode);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(EndpointNotFoundException))]
        public void Customerが削除できるべき()
        {
            using (WebChannelFactory<ICustomers> cf = new WebChannelFactory<ICustomers>(ClientBaseUri))
            {
                ICustomers channel = cf.CreateChannel();
                channel.DeleteCustomer(aliceId_);
                channel.GetCustomer(aliceId_);
            }
        }

        [TestMethod]
        public void 生HTTPのDELETEでCustomerが削除できるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, aliceId_));
            request.Method = "DELETE";
            request.ContentType = "application/xml; charset=utf-8";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            Assert.AreEqual<HttpStatusCode>(HttpStatusCode.OK, response.StatusCode);

            using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
            {
                Assert.AreEqual<string>(string.Empty, reader.ReadToEnd());
            }
        }

        [TestMethod]
        public void Customerが存在しない場合の削除はHTTPステータス404が返されるべき()
        {
            UriTemplate template = new UriTemplate("customers/{id}");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(template.BindByPosition(ClientBaseUri, "9"));
            request.Method = "DELETE";
            request.ContentType = "application/xml; charset=utf-8";

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.Fail();
            }
            catch (WebException ex)
            {
                Assert.AreEqual<WebExceptionStatus>(WebExceptionStatus.ProtocolError, ex.Status);
                Assert.AreEqual<HttpStatusCode>(HttpStatusCode.NotFound, ((HttpWebResponse)ex.Response).StatusCode);
            }
        }
    }
}