Bagikan melalui


Menirukan Kerangka Kerja Entitas saat Pengujian Unit ASP.NET Web API 2

oleh Tom FitzMacken

Unduh Proyek yang Selesai

Panduan dan aplikasi ini menunjukkan cara membuat pengujian unit untuk aplikasi Web API 2 Anda yang menggunakan Kerangka Kerja Entitas. Ini menunjukkan cara memodifikasi pengontrol perancah untuk memungkinkan melewati objek konteks untuk pengujian, dan cara membuat objek pengujian yang berfungsi dengan Kerangka Kerja Entitas.

Untuk pengenalan pengujian unit dengan ASP.NET Web API, lihat Pengujian Unit dengan ASP.NET Web API 2.

Tutorial ini mengasumsikan Anda terbiasa dengan konsep dasar ASP.NET Web API. Untuk tutorial pengantar, lihat Memulai ASP.NET Web API 2.

Versi perangkat lunak yang digunakan dalam tutorial

Dalam topik ini

Topik ini berisi bagian berikut:

Jika Anda telah menyelesaikan langkah-langkah dalam Pengujian Unit dengan ASP.NET Web API 2, Anda dapat melompat ke bagian Tambahkan pengontrol.

Prasyarat

Visual Studio 2017 edisi Komunitas, Profesional, atau Perusahaan

Mengunduh kode

Unduh proyek yang telah selesai. Proyek yang dapat diunduh mencakup kode pengujian unit untuk topik ini dan untuk topik Pengujian Unit ASP.NET Web API 2 .

Membuat aplikasi dengan proyek pengujian unit

Anda dapat membuat proyek pengujian unit saat membuat aplikasi atau menambahkan proyek pengujian unit ke aplikasi yang ada. Tutorial ini menunjukkan pembuatan proyek pengujian unit saat membuat aplikasi.

Buat Aplikasi Web ASP.NET baru bernama StoreApp.

Di jendela Proyek ASP.NET Baru, pilih templat Kosong dan tambahkan folder dan referensi inti untuk API Web. Pilih opsi Tambahkan pengujian unit . Proyek pengujian unit secara otomatis bernama StoreApp.Tests. Anda dapat menyimpan nama ini.

membuat proyek pengujian unit

Setelah membuat aplikasi, Anda akan melihatnya berisi dua proyek - StoreApp dan StoreApp.Tests.

Membuat kelas model

Di proyek StoreApp Anda, tambahkan file kelas ke folder Model bernamaProduct.cs. Ganti isi file dengan kode berikut.

using System;

namespace StoreApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

Bangun solusinya.

Menambahkan pengontrol

Klik kanan folder Pengontrol dan pilih Tambahkan dan Item Perancah Baru. Pilih Pengontrol WEB API 2 dengan tindakan, menggunakan Kerangka Kerja Entitas.

tambahkan pengontrol baru

Set nilai ikuti ini:

  • Nama pengontrol: ProductController
  • Kelas model: Produk
  • Kelas konteks data: [Pilih tombol Konteks data baru yang mengisi nilai yang terlihat di bawah ini]

tentukan pengontrol

Klik Tambahkan untuk membuat pengontrol dengan kode yang dibuat secara otomatis. Kode ini mencakup metode untuk membuat, mengambil, memperbarui, dan menghapus instans kelas Produk. Kode berikut menunjukkan metode untuk menambahkan Produk. Perhatikan bahwa metode mengembalikan instans IHttpActionResult.

// POST api/Product
[ResponseType(typeof(Product))]
public IHttpActionResult PostProduct(Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    db.Products.Add(product);
    db.SaveChanges();

    return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}

IHttpActionResult adalah salah satu fitur baru di Web API 2, dan menyederhanakan pengembangan pengujian unit.

Di bagian berikutnya, Anda akan menyesuaikan kode yang dihasilkan untuk memfasilitasi meneruskan objek pengujian ke pengontrol.

Menambahkan injeksi dependensi

Saat ini, kelas ProductController dikodekan secara permanen untuk menggunakan instans kelas StoreAppContext. Anda akan menggunakan pola yang disebut injeksi dependensi untuk memodifikasi aplikasi Anda dan menghapus dependensi yang dikodekan secara permanen. Dengan melanggar dependensi ini, Anda dapat meneruskan objek tiruan saat pengujian.

Klik kanan folder Model , dan tambahkan antarmuka baru bernama IStoreAppContext.

Ganti kode dengan kode berikut.

using System;
using System.Data.Entity;

namespace StoreApp.Models
{
    public interface IStoreAppContext : IDisposable
    {
        DbSet<Product> Products { get; }
        int SaveChanges();
        void MarkAsModified(Product item);    
    }
}

Buka file StoreAppContext.cs dan buat perubahan yang disorot berikut ini. Perubahan penting yang perlu diperhatikan adalah:

  • Kelas StoreAppContext mengimplementasikan antarmuka IStoreAppContext
  • Metode MarkAsModified diimplementasikan
using System;
using System.Data.Entity;

namespace StoreApp.Models
{
    public class StoreAppContext : DbContext, IStoreAppContext
    {
        public StoreAppContext() : base("name=StoreAppContext")
        {
        }

        public DbSet<Product> Products { get; set; }
    
        public void MarkAsModified(Product item)
        {
            Entry(item).State = EntityState.Modified;
        }
    }
}

Buka file ProductController.cs. Ubah kode yang ada agar sesuai dengan kode yang disorot. Perubahan ini merusak dependensi pada StoreAppContext dan memungkinkan kelas lain untuk meneruskan objek yang berbeda untuk kelas konteks. Perubahan ini akan memungkinkan Anda untuk lulus dalam konteks pengujian selama pengujian unit.

public class ProductController : ApiController
{
    // modify the type of the db field
    private IStoreAppContext db = new StoreAppContext();

    // add these constructors
    public ProductController() { }

    public ProductController(IStoreAppContext context)
    {
        db = context;
    }
    // rest of class not shown
}

Ada satu perubahan lagi yang harus Anda lakukan di ProductController. Dalam metode PutProduct , ganti baris yang mengatur status entitas untuk dimodifikasi dengan panggilan ke metode MarkAsModified.

// PUT api/Product/5
public IHttpActionResult PutProduct(int id, Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (id != product.Id)
    {
        return BadRequest();
    }

    //db.Entry(product).State = EntityState.Modified;
    db.MarkAsModified(product);
    
    // rest of method not shown
}

Bangun solusinya.

Anda sekarang siap untuk menyiapkan proyek pengujian.

Menginstal paket NuGet dalam proyek pengujian

Saat Anda menggunakan templat Kosong untuk membuat aplikasi, proyek pengujian unit (StoreApp.Tests) tidak menyertakan paket NuGet yang diinstal. Templat lain, seperti templat API Web, menyertakan beberapa paket NuGet dalam proyek pengujian unit. Untuk tutorial ini, Anda harus menyertakan paket Kerangka Kerja Entitas dan paket Microsoft ASP.NET Web API 2 Core ke proyek pengujian.

Klik kanan proyek StoreApp.Tests dan pilih Kelola Paket NuGet. Anda harus memilih proyek StoreApp.Tests untuk menambahkan paket ke proyek tersebut.

mengelola paket

Dari paket Online, temukan dan instal paket EntityFramework (versi 6.0 atau yang lebih baru). Jika tampaknya paket EntityFramework sudah diinstal, Anda mungkin telah memilih proyek StoreApp alih-alih proyek StoreApp.Tests.

tambahkan Kerangka Kerja Entitas

Temukan dan instal paket Microsoft ASP.NET Web API 2 Core.

menginstal paket inti api web

Tutup jendela Kelola Paket NuGet.

Membuat konteks pengujian

Tambahkan kelas bernama TestDbSet ke proyek pengujian. Kelas ini berfungsi sebagai kelas dasar untuk himpunan data pengujian Anda. Ganti kode dengan kode berikut.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;

namespace StoreApp.Tests
{
    public class TestDbSet<T> : DbSet<T>, IQueryable, IEnumerable<T>
        where T : class
    {
        ObservableCollection<T> _data;
        IQueryable _query;

        public TestDbSet()
        {
            _data = new ObservableCollection<T>();
            _query = _data.AsQueryable();
        }

        public override T Add(T item)
        {
            _data.Add(item);
            return item;
        }

        public override T Remove(T item)
        {
            _data.Remove(item);
            return item;
        }

        public override T Attach(T item)
        {
            _data.Add(item);
            return item;
        }

        public override T Create()
        {
            return Activator.CreateInstance<T>();
        }

        public override TDerivedEntity Create<TDerivedEntity>()
        {
            return Activator.CreateInstance<TDerivedEntity>();
        }

        public override ObservableCollection<T> Local
        {
            get { return new ObservableCollection<T>(_data); }
        }

        Type IQueryable.ElementType
        {
            get { return _query.ElementType; }
        }

        System.Linq.Expressions.Expression IQueryable.Expression
        {
            get { return _query.Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return _query.Provider; }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return _data.GetEnumerator();
        }
    }
}

Tambahkan kelas bernama TestProductDbSet ke proyek pengujian yang berisi kode berikut.

using System;
using System.Linq;
using StoreApp.Models;

namespace StoreApp.Tests
{
    class TestProductDbSet : TestDbSet<Product>
    {
        public override Product Find(params object[] keyValues)
        {
            return this.SingleOrDefault(product => product.Id == (int)keyValues.Single());
        }
    }
}

Tambahkan kelas bernama TestStoreAppContext dan ganti kode yang ada dengan kode berikut.

using System;
using System.Data.Entity;
using StoreApp.Models;

namespace StoreApp.Tests
{
    public class TestStoreAppContext : IStoreAppContext 
    {
        public TestStoreAppContext()
        {
            this.Products = new TestProductDbSet();
        }

        public DbSet<Product> Products { get; set; }

        public int SaveChanges()
        {
            return 0;
        }

        public void MarkAsModified(Product item) { }
        public void Dispose() { }
    }
}

Membuat pengujian

Secara default, proyek pengujian Anda menyertakan file pengujian kosong bernama UnitTest1.cs. File ini menunjukkan atribut yang Anda gunakan untuk membuat metode pengujian. Untuk tutorial ini, Anda dapat menghapus file ini karena Anda akan menambahkan kelas pengujian baru.

Tambahkan kelas bernama TestProductController ke proyek pengujian. Ganti kode dengan kode berikut.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Http.Results;
using System.Net;
using StoreApp.Models;
using StoreApp.Controllers;

namespace StoreApp.Tests
{
    [TestClass]
    public class TestProductController
    {
        [TestMethod]
        public void PostProduct_ShouldReturnSameProduct()
        {
            var controller = new ProductController(new TestStoreAppContext());

            var item = GetDemoProduct();

            var result =
                controller.PostProduct(item) as CreatedAtRouteNegotiatedContentResult<Product>;

            Assert.IsNotNull(result);
            Assert.AreEqual(result.RouteName, "DefaultApi");
            Assert.AreEqual(result.RouteValues["id"], result.Content.Id);
            Assert.AreEqual(result.Content.Name, item.Name);
        }

        [TestMethod]
        public void PutProduct_ShouldReturnStatusCode()
        {
            var controller = new ProductController(new TestStoreAppContext());

            var item = GetDemoProduct();

            var result = controller.PutProduct(item.Id, item) as StatusCodeResult;
            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(StatusCodeResult));
            Assert.AreEqual(HttpStatusCode.NoContent, result.StatusCode);
        }

        [TestMethod]
        public void PutProduct_ShouldFail_WhenDifferentID()
        {
            var controller = new ProductController(new TestStoreAppContext());

            var badresult = controller.PutProduct(999, GetDemoProduct());
            Assert.IsInstanceOfType(badresult, typeof(BadRequestResult));
        }

        [TestMethod]
        public void GetProduct_ShouldReturnProductWithSameID()
        {
            var context = new TestStoreAppContext();
            context.Products.Add(GetDemoProduct());

            var controller = new ProductController(context);
            var result = controller.GetProduct(3) as OkNegotiatedContentResult<Product>;

            Assert.IsNotNull(result);
            Assert.AreEqual(3, result.Content.Id);
        }

        [TestMethod]
        public void GetProducts_ShouldReturnAllProducts()
        {
            var context = new TestStoreAppContext();
            context.Products.Add(new Product { Id = 1, Name = "Demo1", Price = 20 });
            context.Products.Add(new Product { Id = 2, Name = "Demo2", Price = 30 });
            context.Products.Add(new Product { Id = 3, Name = "Demo3", Price = 40 });

            var controller = new ProductController(context);
            var result = controller.GetProducts() as TestProductDbSet;

            Assert.IsNotNull(result);
            Assert.AreEqual(3, result.Local.Count);
        }

        [TestMethod]
        public void DeleteProduct_ShouldReturnOK()
        {
            var context = new TestStoreAppContext();
            var item = GetDemoProduct();
            context.Products.Add(item);

            var controller = new ProductController(context);
            var result = controller.DeleteProduct(3) as OkNegotiatedContentResult<Product>;

            Assert.IsNotNull(result);
            Assert.AreEqual(item.Id, result.Content.Id);
        }

        Product GetDemoProduct()
        {
            return new Product() { Id = 3, Name = "Demo name", Price = 5 };
        }
    }
}

Jalankan pengujian

Anda sekarang siap untuk menjalankan pengujian. Semua metode yang ditandai dengan atribut TestMethod akan diuji. Dari item menu Uji , jalankan pengujian.

jalankan pengujian

Buka jendela Test Explorer , dan perhatikan hasil pengujian.

hasil pengujian