Pure.Krome
Pure.Krome

Reputation: 86957

How do i unit test this business logic?

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...

(psuedo code)

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?

Update

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

Answers (6)

Stuart Caborn
Stuart Caborn

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.

  1. Decorate the domain object with user information
  2. Persist the update logic
  3. Determine the next view to render based on success/failure

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

Kirschstein
Kirschstein

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

David Ly
David Ly

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

Khalid Abuhakmeh
Khalid Abuhakmeh

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

Daniel Br&#252;ckner
Daniel Br&#252;ckner

Reputation: 59645

You might mock _fooService.Save(foo) and inspect the supplied foo.

Upvotes: 1

Brian Agnew
Brian Agnew

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

Related Questions