In an asp.net core application, I have a pair of controller methods that respond to an Edit action. One for Get, which takes a string parameter for the entity id:
public async Task<IActionResult> Edit(string id)
and the other for receiving a post of updated entity values:
[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditSave(string id)
Inside the postable action method, I call
var bindingSuccess = await TryUpdateModelAsync(vm);
And that works fine.
Now, I'm trying to write a test for this, but am finding that TryUpdateModelAsync
requires lots of things from HttpContext
and the controller to be fleshed out. I've tried mocking those out, but after looking at the source code for TryUpdateModelAsync
, I realize that I'd essentially need to mock everything down to the metadata, which isn't proving to be straightforward.
I'm wondering if perhaps this difficulty is telling me something: TryUpdateModelAsync
makes it hard to test, so I should refactor the controller method to not rely on this helper. Instead, I could add another parameter to the method for my viewmodel and decorate it with [FromBody]
, so the model binding would happen from the post fields when present, but I would be able to pass in a view model when testing. However, I like the TryUpdateModelAsync
method, because it does the busy work of merging the post fields into my view model. The other way I can think to accomplish the merging is write my own Merge method. Okay, no big deal, but I'd prefer not to have to do this for each entity (or reinvent the wheel writing a reflection based merger) and really, I'm wondering if I just missed the boat on how to write a unit test against this. I could fire up a whole TestServer
, like I do for integration tests, but I'm not sure this is the right direction and feels like I would just be complicating my unit tests further. However, maybe it is justified in this scenario?
I've seen answers that worked with previous versions of .net mvc, where all they needed to do was mock an IValueProvider
and attach it to the controller, but in .net core it appears the TryUpdateModelAsync
was reworked and requires more moving parts.
In sum, I see three options:
- Continue mocking and stubbing out all the pieces that
TryUpdateModelAsync
needs. This might be a dead end, it seems to be so far. - Use
TestServer
and make this test from a little higher altitude using anHttpClient
- Refactor this method to use
[FromBody]
on a view model parameter, then write my own merge methods for each entity, there by side steppingTryUpdateModelAsync
altogether
These all have their drawbacks, so I'm hoping there's a 4th entry to this list that I don't see because I am ignorant.
Aucun commentaire:
Enregistrer un commentaire