Reputation: 86957
i have a method that does takes in a object and saves it to the database. But, before i save the object, i do the following...
if (IsAuthenticated)
{
foo.UserId = AuthenticatedUser.Id;
}
else
{
foo.AnonEmail = "Jon@World-Domination";
foo.AnonName = "Jon Skeet";
}
try
{
_fooService.Save(foo);
}
catch
{
// Some view, with error stuff now added to
return View(...); ViewData.ModelState.
}
// all good, redirect to the proper next view.
return RedirectToAction(...);
That code works fine, but i'm not sure how to write the two unit tests for a success. a) User is authenticated with valid data b) User is not authentiated with valid data.
The reason why i'm not sure what to do is, is that both scenario return the same RedirectToAction(..) view object. So i can successfully test that .. but it doesn't tell me if the object saved contains the authenticated user id or the anon info. It's like i want the first unit test to say
thoughts?
The common suggestion is that i mock the fooService. I'm currently using Dependency Injection and Moq, so could somone show me how i would use Moq? I'm not sure how the DI is important here, though ???
Upvotes: 3
Views: 1642
Reputation: 186
Maybe you are finding it difficult to test the object because you have more than one activity taking place in a single method.
The overall theme here is controller logic.
If you extract another object (IUserDecoratorService) then your code looks like
userService.UpdateUserInformation(foo);
try
{
_fooService.Save(foo);
}
catch
{
// Some view, with error stuff now added to
return View(...); ViewData.ModelState.
}
// all good, redirect to the proper next view.
return RedirectToAction(...);
This method is simple to test as it is 2 simple interactions with the 2 services and a routing decision which you can already test.
Now you just need to write the tests for your new service:
[Test]
public void ShouldDecorateWithUserIdForAuthenticatedUser()
{
{setup authenticated user}
:
service.UpdateUserInformation(foo);
Assert.AreEqual(expectedId, foo.UserId);
Assert.IsNull(foo.AnonEmail);
Assert.IsNull(foo.AnonEName);
}
[Test]
public void ShouldSpoofTheAllKnowingSkeetIfAnonymousUser()
{
{setup anonymous user}
:
service.UpdateUserInformation(foo);
Assert.AreEqual(UnassignedId, foo.UserId);
Assert.AreEqual("Jon@World-Domination", foo.AnonEmail);
Assert.AreEqual("Jon Skeet", foo.AnonName);
}
Upvotes: 1
Reputation: 14868
You want to use the DI to get the correct implementation of fooservice into your object, so at testing time you can do this;
(Using Moq)
[TestMethod]
public void Jon_Skeet_Is_Saved_If_User_Not_Authenticated()
{
bool jonWasSaved = false;
var mockFooService = new Mock<IFooService>();
mockFooService
.Expect(x => x.Save(It.Is<Foo>(foo => foo.AnonName == "Jon Skeet")))
.Callback(() => jonWasSaved = true;);
FooManager instance = new FooManager(mockFooService.Object);
Foo testFoo = new Foo();
testFoo.UserId = 1; // this is not the authenticated id
instance.baa(foo);
Assert.IsTrue(jonWasSaved);
}
You may also want to pass in a mock version of whatever service you use to check the AuthetnicatedUser.Id
HTH
Upvotes: 0
Reputation: 31586
Do you have access to foo in the test? I'm assuming it's a field on the class. Is there a public getter? If not you may have to use Reflection (I assume that's available in asp.net, but I'm not too familiar with it)
Upvotes: 0
Reputation: 10839
you still have a reference to your object. At the end of your unit test, couldn't you just assert what the value was foo.AnonEmail or UserId?
Unit tests should not touch an external source, (that's an integration test) so if you go that route you should mock your datasource and then test through your mock.
Upvotes: 0
Reputation: 59645
You might mock _fooService.Save(foo) and inspect the supplied foo.
Upvotes: 1
Reputation: 272277
I would mock up the _fooService
object, and test what it receives as part of your test. That way your surrounding code remains the same and is untouched, and by checking what _fooService
receives, you can assert whether the behaviour is as expected. The return object is not of interest in this case.
How do you mock your _fooService
? You can either implement your own 'test' version (adhering to the same interface as the real world version), or using a mocking framework. Whichever approach you use, your code above needs to be configured with a given implementation of the _fooService
(usually on construction - see dependency injection for more info on how this may work)
Upvotes: 3