如何測試(二)
測試主專案(WebApi)
建立測試專案
測試專案名稱:ApiController.Tests
在此專案會需要偽造的物件如下:
- Response
- Request
加入Nuget套件
- NUnit
- Moq
- EntityFramework
- Newtonsoft.Json
- Microsoft.AspNet.WebApi.Core
加入專案參考
- BusinessEntities
- BusinessServices
- DataModel
- TestsHelper
- WebApi
開始實作
加入以下的檔案
ProductControllerTest.cs
透過HttpClient
加入Token至Request的Header去摸擬Authentication(授權)和Authorization(驗證)
using BusinessEntities;
using BusinessServices;
using BusinessServices.Tests;
using DataModel;
using DataModel.UnitOfWork;
using Moq;
using Newtonsoft.Json;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Hosting;
using TestHelper;
using WebApi.Controllers;
using WebApi.ErrorHelper;
namespace ApiController.Tests
{
public class ProductControllerTest
{
#region Variables
private IProductServices _productService;
private ITokenServices _tokenService;
private IUnitOfWork _unitOfWork;
private List<Products> _products;
private List<Tokens> _tokens;
private GenericRepository<Products> _productRepository;
private GenericRepository<Tokens> _tokenRepository;
private WebApiDbEntities _dbEntities;
private HttpClient _client;
private HttpResponseMessage _response;
private string _token;
private const string ServiceBaseURL = "http://localhost:50875/";
#endregion
[OneTimeSetUp]
public void Setup()
{
_products = SetUpProducts();
_tokens = SetUpTokens();
_dbEntities = new Mock<WebApiDbEntities>().Object;
_tokenRepository = SetUpTokenRepository();
_productRepository = SetUpProductRepository();
var unitOfWork = new Mock<IUnitOfWork>();
unitOfWork.SetupGet(s => s.ProductRepository).Returns(_productRepository);
unitOfWork.SetupGet(s => s.TokenRepository).Returns(_tokenRepository);
_unitOfWork = unitOfWork.Object;
_productService = new ProductServices(_unitOfWork);
_tokenService = new TokenServices(_unitOfWork);
_client = new HttpClient { BaseAddress = new Uri(ServiceBaseURL) };
var tokenEntity = _tokenService.GenerateToken(1);
_token = tokenEntity.AuthToken;
_client.DefaultRequestHeaders.Add("Token", _token);
}
[SetUp]
public void ReInitializeTest()
{
_client = new HttpClient { BaseAddress = new Uri(ServiceBaseURL) };
_client.DefaultRequestHeaders.Add("Token", _token);
}
[TearDown]
public void DisposeTest()
{
if (_response != null)
_response.Dispose();
if (_client != null)
_client.Dispose();
}
private GenericRepository<Products> SetUpProductRepository()
{
// Initialise repository
var mockRepo = new Mock<GenericRepository<Products>>(MockBehavior.Default, _dbEntities);
// Setup mocking behavior
mockRepo.Setup(p => p.GetAll()).Returns(_products);
mockRepo.Setup(p => p.GetByID(It.IsAny<int>()))
.Returns(new Func<int, Products>(
id => _products.Find(p => p.ProductId.Equals(id))));
mockRepo.Setup(p => p.Insert((It.IsAny<Products>())))
.Callback(new Action<Products>(newProduct =>
{
var maxProductID = _products.Last().ProductId;
var nextProductID = maxProductID + 1;
newProduct.ProductId = nextProductID;
_products.Add(newProduct);
}));
mockRepo.Setup(p => p.Update(It.IsAny<Products>()))
.Callback(new Action<Products>(prod =>
{
var oldProduct = _products.Find(a => a.ProductId == prod.ProductId);
oldProduct = prod;
}));
mockRepo.Setup(p => p.Delete(It.IsAny<Products>()))
.Callback(new Action<Products>(prod =>
{
var productToRemove =
_products.Find(a => a.ProductId == prod.ProductId);
if (productToRemove != null)
_products.Remove(productToRemove);
}));
// Return mock implementation object
return mockRepo.Object;
}
private GenericRepository<Tokens> SetUpTokenRepository()
{
// Initialise repository
var mockRepo = new Mock<GenericRepository<Tokens>>(MockBehavior.Default, _dbEntities);
// Setup mocking behavior
mockRepo.Setup(p => p.GetAll()).Returns(_tokens);
mockRepo.Setup(p => p.GetByID(It.IsAny<int>()))
.Returns(new Func<int, Tokens>(
id => _tokens.Find(p => p.TokenId.Equals(id))));
mockRepo.Setup(p => p.Insert((It.IsAny<Tokens>())))
.Callback(new Action<Tokens>(newToken =>
{
var maxTokenID = _tokens.Last().TokenId;
var nextTokenID = maxTokenID + 1;
newToken.TokenId = nextTokenID;
_tokens.Add(newToken);
}));
mockRepo.Setup(p => p.Update(It.IsAny<Tokens>()))
.Callback(new Action<Tokens>(token =>
{
var oldToken = _tokens.Find(a => a.TokenId == token.TokenId);
oldToken = token;
}));
mockRepo.Setup(p => p.Delete(It.IsAny<Tokens>()))
.Callback(new Action<Tokens>(prod =>
{
var tokenToRemove =
_tokens.Find(a => a.TokenId == prod.TokenId);
if (tokenToRemove != null)
_tokens.Remove(tokenToRemove);
}));
// Return mock implementation object
return mockRepo.Object;
}
[Test]
public void GetAllProductsTest()
{
var productController = new ProductController(_productService)
{
//摸擬發出的Request
Request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/all")
}
};
//初始化Request會用到
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
//取得ProductController裡回傳的Response
_response = productController.Get();
var responseResult = JsonConvert.DeserializeObject<List<Products>>(_response.Content.ReadAsStringAsync().Result);
Assert.AreEqual(_response.StatusCode, HttpStatusCode.OK);
Assert.AreEqual(responseResult.Any(), true);
var comparer = new ProductComparer();
CollectionAssert.AreEqual(
responseResult.OrderBy(product => product, comparer),
_products.OrderBy(product => product, comparer), comparer);
}
[Test]
public void GetProductByIdTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/productid/2")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
_response = productController.Get(2);
var responseResult = JsonConvert.DeserializeObject<Products>(_response.Content.ReadAsStringAsync().Result);
Assert.AreEqual(_response.StatusCode, HttpStatusCode.OK);
AssertObjects.PropertyValuesAreEquals(responseResult,
_products.Find(a => a.ProductName.Contains("Mobile")));
}
[Test]
//[ExpectedException("WebApi.ErrorHelper.ApiDataException")]
public void GetProductByWrongIdTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/productid/10")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
var ex = Assert.Throws<ApiDataException>(() => productController.Get(10));
Assert.That(ex.ErrorCode, Is.EqualTo(1001));
Assert.That(ex.ErrorDescription, Is.EqualTo("No product found for this id."));
}
[Test]
// [ExpectedException("WebApi.ErrorHelper.ApiException")]
public void GetProductByInvalidIdTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/productid/-1")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
var ex = Assert.Throws<ApiException>(() => productController.Get(-1));
Assert.That(ex.ErrorCode, Is.EqualTo((int)HttpStatusCode.BadRequest));
Assert.That(ex.ErrorDescription, Is.EqualTo("Bad Request..."));
}
/// <summary>
/// Create product test
/// </summary>
[Test]
public void CreateProductTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/Create")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
var newProduct = new ProductEntity()
{
ProductName = "Android Phone"
};
var maxProductIDBeforeAdd = _products.Max(a => a.ProductId);
newProduct.ProductId = maxProductIDBeforeAdd + 1;
productController.Post(newProduct);
var addedproduct = new Products() {
ProductName = newProduct.ProductName,
ProductId = newProduct.ProductId
};
AssertObjects.PropertyValuesAreEquals(addedproduct, _products.Last());
Assert.That(maxProductIDBeforeAdd + 1, Is.EqualTo(_products.Last().ProductId));
}
/// <summary>
/// Update product test
/// </summary>
[Test]
public void UpdateProductTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/Modify")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
var firstProduct = _products.First();
firstProduct.ProductName = "Laptop updated";
var updatedProduct = new ProductEntity() {
ProductName = firstProduct.ProductName,
ProductId = firstProduct.ProductId
};
productController.Put(firstProduct.ProductId, updatedProduct);
Assert.That(firstProduct.ProductId, Is.EqualTo(1)); // hasn't changed
}
/// <summary>
/// Delete product test
/// </summary>
[Test]
public void DeleteProductTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/Remove")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
int maxID = _products.Max(a => a.ProductId); // Before removal
var lastProduct = _products.Last();
// Remove last Product
productController.Delete(lastProduct.ProductId);
Assert.That(maxID, Is.GreaterThan(_products.Max(a => a.ProductId))); // Max id reduced by 1
}
// <summary>
/// Delete product test with invalid id
/// </summary>
[Test]
public void DeleteProductInvalidIdTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/remove")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
var ex = Assert.Throws<ApiException>(() => productController.Delete(-1));
Assert.That(ex.ErrorCode, Is.EqualTo((int)HttpStatusCode.BadRequest));
Assert.That(ex.ErrorDescription, Is.EqualTo("Bad Request..."));
}
/// <summary>
/// Delete product test with wrong id
/// </summary>
[Test]
public void DeleteProductWrongIdTest()
{
var productController = new ProductController(_productService)
{
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/remove")
}
};
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
int maxID = _products.Max(a => a.ProductId); // Before removal
var ex = Assert.Throws<ApiDataException>(() => productController.Delete(maxID + 1));
Assert.That(ex.ErrorCode, Is.EqualTo(1002));
Assert.That(ex.ErrorDescription, Is.EqualTo("Product is already deleted or not exist in system."));
}
private static List<Products> SetUpProducts()
{
var prodId = new int();
var products = DataInitializer.GetAllProducts();
foreach (Products prod in products)
prod.ProductId = ++prodId;
return products;
}
private static List<Tokens> SetUpTokens()
{
var tokId = new int();
var tokens = DataInitializer.GetAllTokens();
foreach (Tokens tok in tokens)
tok.TokenId = ++tokId;
return tokens;
}
[OneTimeTearDown]
public void DisposeAllObjects()
{
_tokenService = null;
_productService = null;
_unitOfWork = null;
_tokenRepository = null;
_productRepository = null;
_tokens = null;
_products = null;
if (_response != null)
_response.Dispose();
if (_client != null)
_client.Dispose();
}
}
}
--
偽造一個Request的方式(放置於ProductControllerTest.cs裡)
var productController = new ProductController(_productService)
{
//摸擬發出的Request(方法一)
Request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(ServiceBaseURL + "v1/Products/Product/all")
}
//摸擬發出的Request(方法二)
Request = new HttpRequestMessage()
};
//初始化Request會用到
productController.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
//取得ProductController裡回傳的Response
_response = productController.Get();
實際發出一個Post的方式(未在ProductControllerTest.cs裡)
//建立HttpClient
var client = new HttpClient { BaseAddress = new Uri(ServiceBaseURL) };
//加入你的Http Header
client.DefaultRequestHeaders.Add("Authorization", "Basic YWtoaWw6YWtoaWw=");
//實際送出HttpPost並取得結果
_response = client.PostAsync("login", null).Result;
//實際送出HttpGet並取得結果
//_response = client.GetAsync("v1/Products/Product/allproducts/").Result;
//response 常見取得結果的方式
//_response.Headers
//_response.Content.ReadAsStringAsync().Result
//_response.StatusCode