Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Oleh Steve Smith
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat menguji unit logika pengontrol, hanya konten dari satu tindakan yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Pengujian unit untuk controller
Siapkan pengujian unit untuk aksi controller agar berfokus pada perilaku controller. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit filter dan rute tersebut secara terpisah, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk mendemonstrasikan uji unit pengontrol, tinjau pengontrol berikut di aplikasi contoh.
Lihat atau unduh contoh kode (cara mengunduhnya)
Controller Home menampilkan daftar sesi brainstorming dan memungkinkan pembuatan sesi brainstorming baru melalui permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Kontroler sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan agar injeksi dependensi (DI) menyediakan instans
IBrainstormSessionRepository. - Dapat diuji dengan layanan yang dimock
IBrainstormSessionRepositorymenggunakan kerangka kerja objek tiruan, seperti Moq. Sebuah objek tiruan adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Unit test untuk aksi ini:
- Mock layanan
IBrainstormSessionRepositorymenggunakan metodeGetTestSessions.GetTestSessionsmembuat dua sesi brainstorming simulasi dengan tanggal dan nama sesi. - Menjalankan metode
Index. - Membuat asersi terhadap hasil yang dikembalikan oleh metode:
- Sebuah ViewResult dikembalikan.
-
ViewDataDictionary.Model adalah
StormSessionViewModel. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode HTTP POST Index pengontrol Home menguji dan memverifikasi bahwa:
- Ketika ModelState.IsValid adalah
false, metode tindakan mengembalikan 400 Bad RequestViewResult dengan data yang sesuai. - Saat
ModelState.IsValidadalahtrue:- Metode
Addpada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Saat ModelState tidak valid, ViewResult yang sama dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk meneruskan model yang tidak valid. Meneruskan model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode action.
Pengujian kedua memverifikasi bahwa ketika ModelState valid:
- Sebuah
BrainstormSessionbaru ditambahkan (melalui repositori). - Metode mengembalikan
RedirectToActionResultdengan properti yang diharapkan.
Pemanggilan tiruan yang tidak dipanggil biasanya diabaikan, tetapi memanggil Verifiable di akhir panggilan penyiapan memungkinkan validasi pemanggilan tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify, yang menyebabkan pengujian gagal jika metode yang diharapkan tidak dipanggil.
Note
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id (ada dua return skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan return terakhir mengembalikan sebuah StormSessionViewModel baru ke tampilan (Controllers/SessionController.cs):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return skenario dalam pengontrol Session Index aksi:
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Beralih ke controller Ideas, aplikasi mengekspos fungsi sebagai API web pada rute api/ideas:
- Daftar ide (
IdeaDTO) yang terkait dengan sesi curah gagasan dikembalikan olehForSessionmetode . - Metode
Createini menambahkan ide baru ke sesi.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Jangan menggandengkan model domain internal aplikasi dengan API yang diekspos ke publik secara tidak perlu.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis menggunakan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk metode API Create dan ForSession dari pengontrol Ideas.
Aplikasi sampel berisi dua ForSession pengujian. Pengujian pertama menentukan apakah ForSession mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
Pengujian kedua ForSession menentukan apakah ForSession mengembalikan daftar ide sesi (<List<IdeaDTO>>) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name benar:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Untuk menguji perilaku Create metode ketika ModelState tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create kedua tergantung pada repositori yang mengembalikan null, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
Tes ketiga Create, Create_ReturnsNewlyCreatedIdeaForSession, memverifikasi bahwa metode repositori UpdateAsync dipanggil. Mock dipanggil dengan Verifiable, dan metode Verify pada repositori mock dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa metode UpdateAsync menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Uji ActionResult<T>
ActionResult<T> (ActionResult<TValue>) dapat mengembalikan tipe yang diturunkan dari ActionResult atau tipe tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO> untuk sesi idtertentu . Jika sesi id tidak ada, pengontrol mengembalikan NotFound:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Dua pengujian ForSessionActionResult controller disertakan dalam ApiIdeasControllerTests.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult tetapi bukan daftar ide yang tidak ada untuk sesi idyang tidak ada :
- Tipe
ActionResultadalahActionResult<List<IdeaDTO>>. - Result adalah sebuah NotFoundObjectResult.
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Sebuah
ActionResultdengan jenisList<IdeaDTO>. -
ActionResult<T>.Value adalah tipe
List<IdeaDTO>. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat Idea baru untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ditemukan.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Tiga pengujian CreateActionResult disertakan dalam ApiIdeasControllerTests.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResultdengan tipeBrainstormSession. -
ActionResult<T>.Result adalah CreatedAtActionResult.
CreatedAtActionResultdianalogikan dengan respons 201 Created dengan headerLocation. -
ActionResult<T>.Value adalah tipe
BrainstormSession. - Pemanggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession), telah dipanggil. Panggilan metodeVerifiablediperiksa dengan menjalankanmockRepo.Verify()di dalam asersi. - Dua
Ideaobjek dikembalikan untuk sesi tersebut. - Item terakhir (yang ditambahkan oleh panggilan tiruan ke
UpdateAsyncpadaIdea) cocok dengan yang ditambahkan ke sesi dalam pengujian olehnewIdea.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Controller memainkan peran sentral dalam aplikasi ASP.NET Core MVC apa pun. Dengan demikian, Anda harus memiliki keyakinan bahwa pengontrol berfungsi sebagaimana mestinya. Pengujian otomatis dapat mendeteksi kesalahan sebelum aplikasi disebarkan ke lingkungan produksi.
Lihat atau unduh contoh kode (cara mengunduhnya)
Pengujian unit logika pengontrol
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat menguji unit logika pengontrol, hanya konten dari satu tindakan yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Siapkan pengujian unit untuk aksi controller agar berfokus pada perilaku controller. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit filter dan rute tersebut secara terpisah, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk mendemonstrasikan uji unit pengontrol, tinjau pengontrol berikut di aplikasi contoh. Controller Home menampilkan daftar sesi brainstorming dan memungkinkan pembuatan sesi brainstorming baru melalui permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Kontroler sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan agar injeksi dependensi (DI) menyediakan instans
IBrainstormSessionRepository. - Dapat diuji dengan layanan yang dimock
IBrainstormSessionRepositorymenggunakan kerangka kerja objek tiruan, seperti Moq. Sebuah objek tiruan adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Unit test untuk aksi ini:
- Mock layanan
IBrainstormSessionRepositorymenggunakan metodeGetTestSessions.GetTestSessionsmembuat dua sesi brainstorming simulasi dengan tanggal dan nama sesi. - Menjalankan metode
Index. - Membuat asersi terhadap hasil yang dikembalikan oleh metode:
- Sebuah ViewResult dikembalikan.
-
ViewDataDictionary.Model adalah
StormSessionViewModel. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode HTTP POST Index pengontrol Home menguji dan memverifikasi bahwa:
- Ketika ModelState.IsValid adalah
false, metode tindakan mengembalikan 400 Bad RequestViewResult dengan data yang sesuai. - Saat
ModelState.IsValidadalahtrue:- Metode
Addpada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Saat ModelState tidak valid, ViewResult yang sama dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk meneruskan model yang tidak valid. Meneruskan model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode action.
Pengujian kedua memverifikasi bahwa ketika ModelState valid:
- Sebuah
BrainstormSessionbaru ditambahkan (melalui repositori). - Metode mengembalikan
RedirectToActionResultdengan properti yang diharapkan.
Pemanggilan tiruan yang tidak dipanggil biasanya diabaikan, tetapi memanggil Verifiable di akhir panggilan penyiapan memungkinkan validasi pemanggilan tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify, yang menyebabkan pengujian gagal jika metode yang diharapkan tidak dipanggil.
Note
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id (ada dua return skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan return terakhir mengembalikan sebuah StormSessionViewModel baru ke tampilan (Controllers/SessionController.cs):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return skenario dalam pengontrol Session Index aksi:
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Beralih ke controller Ideas, aplikasi mengekspos fungsi sebagai API web pada rute api/ideas:
- Daftar ide (
IdeaDTO) yang terkait dengan sesi curah gagasan dikembalikan olehForSessionmetode . - Metode
Createini menambahkan ide baru ke sesi.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Jangan menggandengkan model domain internal aplikasi dengan API yang diekspos ke publik secara tidak perlu.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis menggunakan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk metode API Create dan ForSession dari pengontrol Ideas.
Aplikasi sampel berisi dua ForSession pengujian. Pengujian pertama menentukan apakah ForSession mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
Pengujian kedua ForSession menentukan apakah ForSession mengembalikan daftar ide sesi (<List<IdeaDTO>>) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name benar:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Untuk menguji perilaku Create metode ketika ModelState tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create kedua tergantung pada repositori yang mengembalikan null, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
Tes ketiga Create, Create_ReturnsNewlyCreatedIdeaForSession, memverifikasi bahwa metode repositori UpdateAsync dipanggil. Mock dipanggil dengan Verifiable, dan metode Verify pada repositori mock dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa metode UpdateAsync menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Uji ActionResult<T>
Di ASP.NET Core 2.1 atau yang lebih baru, ActionResult<T> (ActionResult<TValue>) memungkinkan Anda mengembalikan jenis yang berasal dari ActionResult atau mengembalikan jenis tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO> untuk sesi idtertentu . Jika sesi id tidak ada, pengontrol mengembalikan NotFound:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Dua pengujian ForSessionActionResult controller disertakan dalam ApiIdeasControllerTests.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult tetapi bukan daftar ide yang tidak ada untuk sesi idyang tidak ada :
- Tipe
ActionResultadalahActionResult<List<IdeaDTO>>. - Result adalah sebuah NotFoundObjectResult.
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Sebuah
ActionResultdengan jenisList<IdeaDTO>. -
ActionResult<T>.Value adalah tipe
List<IdeaDTO>. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat Idea baru untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ditemukan.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Tiga pengujian CreateActionResult disertakan dalam ApiIdeasControllerTests.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResultdengan tipeBrainstormSession. -
ActionResult<T>.Result adalah CreatedAtActionResult.
CreatedAtActionResultdianalogikan dengan respons 201 Created dengan headerLocation. -
ActionResult<T>.Value adalah tipe
BrainstormSession. - Pemanggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession), telah dipanggil. Panggilan metodeVerifiablediperiksa dengan menjalankanmockRepo.Verify()di dalam asersi. - Dua
Ideaobjek dikembalikan untuk sesi tersebut. - Item terakhir (yang ditambahkan oleh panggilan tiruan ke
UpdateAsyncpadaIdea) cocok dengan yang ditambahkan ke sesi dalam pengujian olehnewIdea.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Konten terkait
- Pengujian integrasi di ASP.NET Core
- Membuat dan menjalankan pengujian unit dengan Visual Studio
- MyTested.AspNetCore.Mvc - Library Pengujian dengan Antarmuka Fluent untuk ASP.NET Core MVC: Library pengujian unit bertipe kuat, menyediakan antarmuka fluent untuk menguji aplikasi MVC dan API web. (Tidak dipertahankan atau didukung oleh Microsoft.)
- JustMockLite: Framework mocking untuk pengembang .NET. (Tidak dipertahankan atau didukung oleh Microsoft.)
ASP.NET Core