I am new to testing and am now learning to use xUnit.
I have written the below test which works and gives the results I am looking for, but I think that I should be able to make it cleaner, like when using TestCase in nUnit. Therefore I am trying to use InlineData, which I understand is xUnits "TestCase", but I don't know how to do it.
When trying to set it up, as shown below (please see the outcommented lines), then it tells me
[Theory]
// [InlineData("")] // wanna use InlineData just to make the code/test cleaner.
// [InlineData(null)]
public async Task Get_Return_Something(
MyController sut)
{
var result1 = await sut.Get(""); // when placing "sut" as param, I get: cannot convert from MyController to string.
var result2 = await sut.Get(null); // same applies here..
result1.ShouldBeOfType(typeof(OkObjectResult));
result2.ShouldBeOfType(typeof(BadRequestObjectResult));
}
My question is - how do I implement the InlineData so that the test is cleaner. And is it even possible when I have a controller as a parameter?
Just to clarify it -> should there be a way to use the syntax from nUnit:
// something like this
[TestCase("", OkObjectResult)]
[TestCase(null, BadRequestObjectResult)]
public async Task Get_Return_Something(MyController sut, param1, param2)
{
var result = await.sut.Get(param1);
result.ShouldBeOfType(typeof(param2));
}
Maybe the nUnit example is incorrect - but I'm trying to point out, what I am looking for, by saying cleaner code. By following the TestCase-logic, I can easily expand my test to 10-15 different TestCases. And I've heard that xUnit does something similar with InlineData (which LasseVågsætherKarlsen told me wasn't possible) - or then MemberClass or ClassData.
Possibility 1
Create a MyControllerClassData
public class MyControllerClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new MyController
{
Id = 1,
// ...
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Modify your Theory
[Theory]
[ClassData(typeof(MyControllerClassData))]
public async Task Get_Return_Something(MyController sut)
{
var result1 = await sut.Get(""); // when placing "sut" as param, I get: cannot convert from MyController to string.
var result2 = await sut.Get(null); // same applies here..
result1.ShouldBeOfType(typeof(OkObjectResult));
result2.ShouldBeOfType(typeof(BadRequestObjectResult));
}
Possibility 2
Create the MyController class in the constructor and store it on a private property
Change the parameters to string and the objectResult that you want
Use always the same MyController, use the string to do the Get() and validate it against the objectResult expected.
This solution seems a lit bit cleaner, as you have one test for each situation.
Finally I managed to get it fixed. It IS actually possible to use InlineData - just not as easy (imo) as with nUnit. Here is the solution:
[Theory]
[InlineData("", typeof(OkObjectResult))]
[InlineData(null, typeof(BadRequestObjectResult))]
public async Task Get_Return_Something(param1, param2){
// moq MyController with its parameters/objects
...
var result = await sut.Get(param1);
result.ShouldBeOfType(param2);
}
In my opinion a very easy way to do it - compared to the ones found when searching the net - hope it is useful for other too.
Related
New to Unit testing, I have below sample code and I want to create a unit test for this , Please suggest what should i do to create a unit test for this ? any link or pointers would be helpful to start
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
Edit: using MSTest for Unit testing
First, you need a test project alongside with your regular project.
You can pick from these three:
MSTest
nUnit
xUnit
All of these should have a project template in VS2022.
xUnit is a popular one, so let's pick that. The usual naming convention for test projects is YourProject.Tests. Rename UnitTest1.cs class to UserNotificationTests.cs.
As simple as it gets, you can now start writing your tests. In xUnit, a method with [Fact] attribute is a test method.
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
}
}
}
Don't think these methods as the methods in the code, naming should be close to English sentences and should reveal the intent as a regular sentence.
Classic approach to unit testing has three phases:
Arrange: Take instances of your objects, set your expected output, mock dependencies, make them ready.
Act: Call the actual action you want to test.
Assert: Check if how your actual output relates to your expected output.
Let's start with arranging.
We need a new instance of UserNotification class so we can call Execute().
We need any dummy IWorkContext object so we can pass it. We'll use NSubstitute library for that.
// Don't forget to add using NSubstitute
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
Now you act, and invoke your method:
// Act
Action act = () => userNotification.Execute(workContext);
And lastly we assert. I highly recommend FluentAssertations library for asserting.
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
Navigate to View > Test Explorer and run your tests, you should see something similar to this:
Congratulations, you wrote your first unit test.
Here's the final version of your test code:
using FluentAssertions;
using NSubstitute;
using System;
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
// Act
Action act = () => userNotification.Execute(workContext);
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
}
}
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
public abstract class Work
{
public virtual void Execute(IWorkContext iwc) { }
}
public interface IWorkContext
{
public string Name { get; set; }
public string Dept { get; set; }
}
public class InvalidWorkException : System.Exception
{
public InvalidWorkException() { }
public InvalidWorkException(string message) : base(message) { }
public InvalidWorkException(string message, System.Exception inner) : base(message, inner) { }
protected InvalidWorkException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
Writing tests feels a lot different than writing regular code. But in time you'll get the hang of it. How to mock, how to act, how to assert, these may vary depending on what you are testing. The main point is to isolate the main thing you want to unit test, and mock the rest.
Good luck!
Because your title mentions specifically that you're trying to test a method with a void return type; I infer that you've already been testing methods with actual return values, and therefore that you already have a test project and know how to run a test once it is written. If not; the answer written by Mithgroth is a good explanation on how to get started on testing in general.
Your test is defined by the behavior that you wish to test. Your snippet has no behavior, which makes it hard to give you a concrete answer.
I've opted to rewrite your example:
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
var splines = iwc.GetSplines();
iwc.Reticulate(splines);
}
}
Now we have some behavior that we want to test. The test goal is to answer the following question:
When calling Execute, does UserNotification fetch the needed splines and reticulate them?
When unit testing, you want to mock all other things. In this case, the IWorkContext is an external dependency, so it should be mocked. Mocking the work context allows us to easily configure the mock to help with the testing. When we run the test, we will pass an IWorkContext object which acts as a spy. In essence, this mocked object will:
... have been set up to return a very specific set of splines, one that we chose for the test's purpose.
... secretly record any calls made to the Reticulate method, and tracks the parameters that were passed into it.
Before we get into the nitty gritty on how to mock, we can already outline how our test is going to go:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IWorkContext mockedContext = ...; // This comes later
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert
// Confirm that Reticulate() was called
// Confirm that Reticulate() was given the result from `GetSplines()`
}
There's your basic unit test. All that's left is to create our mock.
You can write this yourself if you want. Simply create a new class that implements IWorkContext, and give it some more public properties/methods to help you keep track of things. A very simple example would be:
public class MockedWorkContext : IWorkContext
{
// Allows the test to set the returned result
public IEnumerable<Spline> Splines { get; set; }
// History of arguments used for calls made to Reticulate.
// Each call will add an entry to the list.
public List<IEnumerable<Spline>> ReticulateArguments { get; private set; } = new List<IEnumerable<Spline>>();
public IEnumerable<Spline> GetSplines()
{
// Returns the preset splines that the test configured
return this.Splines;
}
// Mocked implementation of Reticulate()
public void Reticulate(IEnumerable<Spline> splines)
{
// Does nothing except record what you passed into it
this.ReticulateArguments.Add(splines);
}
}
This is a very simplified implementation, but it gets the job done. The test will now look like this:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IEnumerable<Spline> splines = new List<Spline>() { new Spline(), new Spline() }; // Just create some items here, it's random test data.
IWorkContext mockedContext = new MockedWorkContext();
mockedContext.Splines = splines;
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert - Confirm that Reticulate() was called
mockedContext.ReticulateArguments.Should().HaveCount(1);
// Confirm that Reticulate() was given the result from `GetSplines()`
mockedContext.ReticulateArguments[0].Should().BeEquivalentTo(splines);
}
This test now exactly tests the behavior of your method. It uses the mocked context as a spy to report on what your unit under test (i.e. UserNotification) does with the context that you pass into it.
Note that I am using FluentAssertions here, as I find it the most easily readable syntax. Feel free to use your own assertion logic.
While you can write your own mocks; there are mocking libraries that help cut down on the boilerplating. Moq and NSubstitute are the two biggest favorites as far as I'm aware. I personally prefer NSubstitute's syntax; but both get the job done equally well.
If you want to use nunit the documentation with example is pretty easy to follow, link below.
Nunit documentation
And I think all other unit test framework have something similar to this.
[Test]
public void Execute_WhenCalled_ThrowArgumentException()
{
//Initialize an instance of IWorkContext
var iwc = new WorkContext();
//or use a Mock object, later on in assert use
//userNotification.Execute(iwc.Object)
var iwc = new Mock<IWorkContext>();
var userNotification = new UserNotification();
Assert.Throws(typeof(InvalidWorkException), () =>
{
userNotification.Execute(iwc)
});
}
My question is very similar to this one:
How to unit-test an action, when return type is ActionResult?
The problem is that my question mixes in the generic ActionResult<T> type, async, and Ok(...). I can't seem to adapt linked question's answer to the generic situation. Or possibly my scenario is subtly different.
Here's a repro. Create new ASP.NET Core Web Application of "API" type. Add a new xUnit .NET Core test project to the solution, that references the API project (as well as any needed framework libs). Create the controller and tests like this, respectively:
public class Thing { public string Name => "Foobar"; }
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
public async Task<ActionResult<Thing>> Get()
{
// The real "Thing" would be asynchronously retrieved from the DB
return Ok(new Thing());
}
}
[Fact]
public async Task Test1()
{
var controller = new ValuesController();
var actionResult = await controller.Get();
Assert.NotNull(actionResult.Value);
Assert.Equal("Foobar", actionResult.Value.Name);
}
Instead of turning green, this test fails on the NotNull assertion (or, if I would not have that assertion, it throws a NullReferenceException).
After debugging and inspecting the class hierarchy, I found that this seems to give the desired results:
[Fact]
public async Task Test1()
{
var controller = new ValuesController();
var actionResult = await controller.Get();
var okResult = actionResult.Result as OkObjectResult;
var realResult = okResult?.Value as Thing;
Assert.Equal("Foobar", realResult?.Name);
}
But this feels like I'm doing something wrong. Practically, I'm left with two related questions:
Is there another idiomatic way to write this test, that collapses all those as casts?
Why does the first example compile, yet give a runtime exception? What's going on here?
With XUnit, you can use T t = Assert.IsType<T>(other). That will do the casting if possible, but otherwise it will cause the test to fail.
For instance I do something like this:
IActionResult actionResult = await Controller.GetItem(id);
OkObjectResult okObjectResult = Assert.IsType<OkObjectResult>(actionResult);
Model model = Assert.IsType<Model>(okObjectResult.Value);
Assert.Equal(id, model.Id);
As for your 2nd question, things can throw null reference exceptions at runtime and compile correctly. That problem will be solved with C# 8 and non-nullable types.
Sorry for this orrible title, i did my best.
Maybe someone with more imagination can help me and this title.
I am a newbie in Unit Testing and I am a bit lost. I am reading "The Art of Unit Tesing" anyway.
I am using Entity Framework 6 for a WebApi application.
In this applicantion i use this pattern:
Api calls Service that calls Repository that return data to service that return data to API.
So, I have a tons of this methods:
API:
public async Task<IHttpActionResult> GetUserById(Guid id)
{
try
{
UserService userService = new UserService();
return Ok(await userService.GetById(id));
}
catch(Exception ex)
{
return InternalServerError(ex);
}
}
Service:
public class UserService
{
private IUserRepository userRepo;
public IUserRepository userRepo { get => userRepo; set => userRepo = value }
public async Task<AspNetUser> GetById(Guid id)
{
if(id == Guid.Empty() || id == null)
return null;
return await Task.Run(() => userRepo.GetById(id));
}
}
Repo:
public interface IUserRepository
{
Task<AspNetUser> GetById(Guid id);
}
public class UserRepository: IUserRepository
{
public async Task<AspNetUser> GetById(Guid id)
{
return DbSet.Where(i => i.Id == id).FirstOrDefault();
}
}
What can be faulty here? what can go wrong? What i should test?
The goal of unit testing is to test each module (usually equates to a method) of code in isolation. This is only possible with loose coupling. In your existing code you do not use DI (dependency injection) and you instantiate service / repository classes in your methods. This makes testing this code in isolation difficult if not almost impossible. Writing unit tests often reveals design/code flaws like these. Refactor your code as follows:
Use interfaces, you did it on your repository but not on your service but that is actually the best place to use an interface.
Use dependency injection. This will make testing much easier.
Why are you using a repository pattern around EF? EF's DbSet is a generic implementation of a repository pattern and DbContext is an implementation of Unit of Work. I strongly recommend not trying to re-encapsolate these in your own half baked abstractions, you are only going to end up making it more difficult for your self to use your own constructs.
That is not the correct way to use async/await. Call through to an async method and if non exists then do not create an async method to begin with (this does not apply to cases where you want to use TPL). In your case you should call through to FirstOrDefaultAsync (or possible SingleOrDefaultAsync);
Name your async methods with the suffix Async, this is considered proper convention.
UserController.cs
private readonly IUserService userService;
public UserController(IUserService userService){
this.userService = userService;
}
public async Task<IHttpActionResult> GetUserById(Guid id)
{
try
{
return Ok(await userService.GetByIdAsync(id));
}
catch(Exception ex)
{
return InternalServerError(ex);
}
}
UserService.cs
public interface IUserService{
Task<AspNetUser> GetByIdAsync(Guid id);
}
public class UserService : IUserService
{
// you can leave MyDbContext unsealed or you can use an interface on this as well depending on your needs
private readonly MyDbContext dbContext;
public UserService(MyDbContext dbContext) {
this.dbContext = dbContext;
}
public Task<AspNetUser> GetByIdAsync(Guid id)
{
if(id == Guid.Empty())
return Task.FromResult(null as AspNetUser);
return dbContext.AspNetUsers.SingleOrDefaultAsync(user => user.Id == id);
}
}
Having done this now you can unit test your code. You can use a popular fake/mock/substitute framework and do the following:
Create a test for your api's GetUserById by creating a mock IUserService instance with custom behavior on GetByIdAsync
Create a test for the UserService::GetByIdAsync method by creating a mock of MyDbContext, you can supply a collection of AspNetUser instances and test which one is returned when calling the method or even test the expression that the DbSet is called with.
As far as what you should test depends on possible inputs and the corresponding expected results. For example : What happens when you pass in an empty GUID into GetByIdAsync? What does the API do when a null result is returned in the Task? etc.
Frankly, this is likely NOT the "best" answer here and I am up voting others.
As this is getting a bit "long" for comments so I am posting as an answer with the "what" more so than the "how" here as Igor has done - good stuff there!
This will be somewhat generic in that regard.
Test what I call the "happy path" i.e. a valid GUID (in your instance) gets something valid.
Perhaps something valid from a string:
Guid g = new Guid("11223344-5566-7788-99AA-BBCCDDEEFF00");
Test something invalid (not present for example)
Guid g = new Guid(someinvalidthingthatparses);
New:
Guid g = Guid.NewGuid();
all 0's i.e. Guid.Empty
Test uninitiated values
In a perfect world, some of these should not get to the method being tested but what if they do? How does it react?
To extrapolate further, I digress but if you are testing something for example that passes an integer as Int32 that might only accept positive values test the boundaries i.e. 0, 1, -1, Int32.MaxValue, Int32.MinValue Int32.MaxValue - 1 Int32.MinValue + 1, begin to think (as you appear to be doing) how to break it. IF you run into a failure condition later, add a unit test for that for example. If you are passing something that will be in a repo somewhere, also consider the limits of that - for example date min/max differs in sql vs C# and by date vs DateTime etc. BeginDate/EndDate - is it inclusive or not etc.
Now each of those "FAILURE" points should do something, how is this handled by the callers/API code? How does it respond if an error is thrown?
-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.
Here's what I have in my test so far:
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void WebClient_Should_Download_From_Correct_Endpoint()
{
const string correctEndpoint = "http://api.rottentomatoes.com/api/public/v1.0/movies/{movie-id}.json?apikey={your-api-key}";
ApiEndpoints.Endpoints["IndividualMovie"].ShouldEqual(correctEndpoint);
}
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd");
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
}
My FIndMovieById method goes online and fetches a JSON result, and that means it sort of breaks the principle behind unit testing. I have a feeling I have to mock this string response, but I don't really know how to approach this.
How would you approach this particular unit testing?
In your second [Test], I would suggest not focusing on a specific return value from your FindMovieById method, unless you truly want to test that your given inputs should always result in "Gone With the Wind". The test that you have seems to be a very specific test case in which a specific input number results in a specific output, which is something that may or may not change when running against your actual database. Also, since you're not going to be testing against the actual web service, doing this kind of validation is basically self-serving - you're not really testing anything. Instead, focus on testing how the Tomato class handles validation of the argument (if at all), and that the Tomato class actually invokes the service to get the return value. Rather than testing specific inputs and outputs, test the behavior of the class, so that if someone changes it in the future, the test should break to alert them that they may have broken working functionality.
For example, if you have input validation, you could test that your Tomato class throws an exception if an invalid input is detected.
Assuming that your Tomato class has some sort of web client functionality for requesting and retrieving the results, you could plug in some stub implementations of the actual web code, or mocked implementations to ensure that Tomato is in fact calling the appropriate web client code to request and process the response.
First off, you might not have to mock to test you code. For example, if you are just testing that you can deserialize JSON into a Movie object, you could just do that by testing a public or internal ParseJSON recond on the Movie class.
However, since you are asking about mocking, here's a quick overview of one way you could write this test using a mock. As it is written, Movie_Information_Is_Loaded_Correctly() looks like an integration test. To turn this into a unit test, you could mock out the web request the Tomato class makes. One way to do that would be to create a ITomatoWebRequester interface and pass that as a parameter to the Tomato class in the constructor. You could then mock the ITomatoWebRequester to return the web response you are expecting, and then you could test that the Tomato class properly parses that response.
The code could look something like this:
public class Tomato
{
private readonly ITomatoWebRequester _webRequester;
public Tomato(string uniqueID, ITomatoWebRequester webRequester)
{
_webRequester = webRequester;
}
public Movie FindMovieById(int movieID)
{
var responseJSON = _webRequester.GetMovieJSONByID(movieID);
//The next line is what we want to unit test
return Movie.Parse(responseJSON);
}
}
public interface ITomatoWebRequester
{
string GetMovieJSONByID(int movieID);
}
To test, you could use a mocking framework like Moq to create a ITomatoWebRequester that will return a result you expect. To do that with Moq the following code should work:
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
var mockWebRequester = new Moq.Mock<ITomatoWebRequester>();
var myJson = "enter json response you want to use to test with here";
mockWebRequester.Setup(a => a.GetMovieJSONByID(It.IsAny<int>())
.Returns(myJson);
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd",
mockWebRequester.Object);
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
The cool thing about the mock in this case is that you don't have to worry about all the hoops the actual ITomatoWebRequester has jump through to return the JSON it is supposed to return, you can just create a mock right in your test that returns exactly what you want. Hopefully this answer serves as a decent intro to mocking. I would definitely suggest reading up on mocking frameworks to get a better feel for how the process works.
Use Rhino.Mocks library and call Expectations where ever appropriate. Following is a sample mocking your movie object.
using System;
using NUnit.Framework;
using Rhino.Mocks;
namespace ConsoleApplication1
{
public class Tomato
{
public Tomato(string t4qpkcsek5h6vgbsy8k4etxdd)
{
//
}
public virtual Movie FindMovieById(int i)
{
return null;
}
}
public class Movie
{
public string Title;
public Movie( )
{
}
public void FindMovieById(int i)
{
throw new NotImplementedException();
}
}
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
//Create Mock.
Tomato tomato = MockRepository.GenerateStub<Tomato>("t4qpkcsek5h6vgbsy8k4etxdd");
//Put expectations.
tomato.Expect(t=>t.FindMovieById(0)).IgnoreArguments().Return(new Movie(){Title ="Gone With The Wind"});
//Test logic.
Movie movie = tomato.FindMovieById(9818);
//Do Assertions.
Assert.AreEqual("Gone With The Wind", movie.Title);
//Verify expectations.
tomato.VerifyAllExpectations();
}
}
}