How to use application variables in UnitTest asp.net - c#

I have a method that uses Application variables to get information from an external file. Since Application variables are not used in unit tests, is there a way I can get the Application variables values from my Global.asax file and be able to use them in the test?
This is my test method:
[TestMethod]
public void TestGetCompanyList()
{
var accController = new AccSerController();
CInt cInt = new CInt();
cIn.Iss = "Other";
cIn.Tick = "BK";
var result
= accController.Clist(cIn) as IEnumerable<CList>;
Assert.IsNotNull(result);
}

Use the repository pattern. Your controller shouldn't have any idea about WebConfiguration.
//This defines the stuff that your controller needs (that your repository should contain)
public interface ISiteConfiguration
{
string Setting1 {get; set;}
}
//Use this in your site. Pull configuration from external file
public class WebConfiguration : ISiteConfiguration
{
public string Setting1 {get; set;}
public WebConfiguration()
{
//Read info from external file here and store in Setting1
Setting1 = File.ReadAllText(HttpContext.Current.Server.MapPath("~/config.txt"));
}
}
//Use this in your unit tests. Manually specify Setting1 as part of "Arrange" step in unit test. You can then use this to test the controller.
public class TestConfiguration : ISiteConfiguration
{
public string Setting1 {get; set;}
}
I'm using Ninject to perform dependency injection, but there's lots of other libraries out there. I'm going to omit some basic Ninject setup from my answer, because there's plenty of resources out there. But the below code shows how you'd specify in your web application to use WebConfiguration to fulfill the needs of an ISiteConfiguration.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISiteConfiguration>().To<WebConfiguration>();
}
Here's where the magic happens. When an instance of your controller is created in your web application, Ninject will look at the constructor and see that it's asking for ISiteConfiguration. And in your Ninject configuration, you told it to use WebConfiguration when it needs ISiteConfiguration. So Ninject will create a new instance of WebConfiguration and provide (inject) it to your controller.
public class AccountServiceController
{
ISiteConfiguration Config {get; set;}
//This is called constructor injection
public AccountServiceController(ISiteConfiguration config)
{
Config = config;
}
public ActionResult Index()
{
//Now you can use Config without needing to know about ISiteConfiguration's implementation details
//Get settings from Config instead of Application
}
}
You can also use Ninject in unit testing, but here's a simpler demo where we're not using it:
[TestMethod]
public void TestGetCompanyList()
{
//Arrange
var config = new TestConfiguration(){ Setting1 = "mysetting" };
var accountController = new AccountServiceController(config);
}
The result of all this is that you can use your controller's action methods easily for unit testing, because you can use whatever implementation of ISiteConfiguration you want.

I've done the following on some of my tests. Not ideal but it gets the job done.
if (System.Web.HttpContext.Current != null)
{
// Fill your application variable
}
else
{
// Get your data from somewhere else
}

There are two ways of unit testing such scenarios as far as I know.
First one is based on splitting controller function into two: one is controller function itself, another one implements the logic (e.g.: this is the one you test). Example:
Before:
public void MyControllerFunction()
{
var x = Context["variable"];
do-something-with-x;
}
After:
public void MyControllerFunction()
{
var x = Context["variable"];
MyControllerLogic(x);
}
internal void MyControllerLogic(object x)
{
do-something-with-x;
}
And then you test MyControllerLogic() function instead of MyControllerFunction() in unit test
Another methodology is create a surrogate context before invoking unit test.
Example:
var controller = new MyController();
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
controller.Request.Content = new StringContent("{ x: 21 }",
Encoding.Unicode);
controller.Request.Content.Headers.ContentType.MediaType =
"application/json";
Please note, I did not create HttpContext in 2nd example, I'm not sure if it's a requirement to have. You probably should be able to create it in similar way as well as the other variables you use. It's sort of a hack anyway, so treat it as such

Related

unit test for a method that takes an instance through Resolve<T> of Autofac

I'm facing a problem trying to implement a unit test for a method on a service.
The architecture of the project is a little bit cumbersome, to say the less...
The problem is that within the method to test it calls another method to take an instance of another service, here is the little monster:
public void SendOrderEmail(string orderCode)
{
Order order= GetOrderService().SerachByCode(orderCode);
.... Send email with the order ....
}
private IOrderService GetOrderService()
{
return OrderService = AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
Please, don't ask why a service calls another service or why is that service not injected at the constructor, as i said the architecture of this project is weird in some points.
I just need to know what is the way to implement a unit test for a method like that.
Thank you!
I would refactor a little the code, let the class that implement this method have IOrderService injected through the constructor, save the instance and then use it,
this way you can inject your fake IOrderService during the test (or use Automock) :)
If you really really can't change the constructor, you can use a property to set IOrderService
---------------- edit
Since i got some downvote on this answer I've tried to get to understand better what is going on.
I'm not sure about this, but seems like you can't edit this class you wrote about, you just want to test it.
Well if that is the case i think i can still give you some advices.
Advice number one: make a test project, link the class file, make a new file with a class like the following one.
class AutofacDependencyResolver {
public static Current { get; private set; }
public ILifetimeScope ApplicationContainer { get; private set; }
public AutofacDependencyResolver(ILifetimeScope scope) {
Current = this;
ApplicationContainer = scope;
}
}
Since the class you need to test is linked it's gonne to compile it and you just can now achieve what you need.
The other (and i think better) advice is do not test stuff you did not wrote / can't modify. What i'm suggesting is writing an adapter, so a class that use the one you can't modify as a black box.
In this case i think you need to test the email, so just check the email output the address stuff like that and ignore the rest.
the people who wrote those classes should have followed solid principles...
As others have said, and you're probably aware yourself anyway, you really want to refactor classes like this and use constructor injection if at all possible. Service location is generally considered an anti-pattern (https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) and it specifically makes unit testing like this harder and less transparent.
However, if you absolutely can't refactor, you can still make methods like this somewhat testable by just providing different registrations for the services you're accessing via service location.
In your case, if you have:
public class EmailSender
{
public void SendOrderEmail(string orderCode)
{
Order order = GetOrderService().SearchByCode(orderCode);
//....Send email with the order ....
}
private IOrderService GetOrderService()
{
return AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
}
...and you're looking to specifically run unit tests over SendOrderEmail to validate the logic surrounding your IOrderService implementation (which could be easily covered by a separate test), the other classes implied there might look like:
public class AutofacDependencyResolver // this is problematic but we can't change it
{
public AutofacDependencyResolver(IContainer applicationContainer)
{
ApplicationContainer = applicationContainer;
}
public IContainer ApplicationContainer { get; }
public static AutofacDependencyResolver Current { get; private set; }
public static void SetContainer(IContainer container)
{
Current = new AutofacDependencyResolver(container);
}
}
public static class ContainerProvider // this sets up production config across your app
{
public static IContainer GetProductionContainer()
{
var builder = new ContainerBuilder();
builder.RegisterType<RealOrderService>()
.As<IOrderService>();
// register all other real dependencies here
return builder.Build();
}
}
With that setup, you only need to provide mocks which are required for the specific method you're testing, assuming you can set your container within AutofacDependencyResolver easily in order to have production and test configuration running in parallel. That might look like the following, using xUnit, Moq and Autofac in a test project:
public class EmailSenderTests
{
private readonly Mock<IOrderService> _orderService;
public EmailSenderTests()
{
// to set up the test fixture we'll create a mock OrderService and store a reference to the mock itself for validation later on
_orderService = new Mock<IOrderService>();
var mockOrder = new Order();
_orderService.Setup(os => os.SearchByCode(It.IsAny<string>()))
.Returns(mockOrder);
}
private IContainer GetTestContainer()
{
// here we're adding just one registration we need, setting the mocked OrderService instance to be used for IOrderService
var builder = new ContainerBuilder();
builder.Register(c => _orderService.Object)
.As<IOrderService>();
return builder.Build();
}
[Fact]
public void SendEmail()
{
AutofacDependencyResolver.SetContainer(GetTestContainer()); // set the test container on the global singleton
var sender = new EmailSender();
sender.SendOrderEmail("abc"); // internally the email sender will retrieve the mock IOrderService via service location
// make any assertions here, e.g.
_orderService.Verify(os=>os.SearchByCode("abc"), Times.Exactly(1));
}
}

How do I spy on a method from a sealed library class? [duplicate]

I have an MVC web app, and I'm using Simple Injector for DI. Almost all my code is covered by unit tests. However, now that I've added some telemetry calls in some controllers, I'm having trouble setting up the dependencies.
The telemetry calls are for sending metrics to the Microsoft Azure-hosted Application Insights service. The app is not running in Azure, just a server with ISS. The AI portal tells you all kinds of things about your application, including any custom events you send using the telemetry library. As a result, the controller requires an instance of Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors. I tried registering it like so (the hybrid lifestyle is unrelated to this question, I just included it for completeness):
// hybrid lifestyle that gives precedence to web api request scope
var requestOrTransientLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
Lifestyle.Transient);
container.Register<TelemetryClient>(requestOrTransientLifestyle);
The problem is that since TelemetryClient has 2 constructors, SI complains and fails validation. I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated. First I wanted to back up and ask this question:
If I don't make the TelemetryClient an injected dependency (just create a New one in the class), will that telemetry get sent to Azure on every run of the unit test, creating lots of false data? Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Any "Insights" into this issue would be much appreciated!
Thanks
Application Insights has an example of unit testing the TelemetryClient by mocking TelemetryChannel.
TelemetryChannel implements ITelemetryChannel so is pretty easy to mock and inject. In this example you can log messages, and then collect them later from Items for assertions.
public class MockTelemetryChannel : ITelemetryChannel
{
public IList<ITelemetry> Items
{
get;
private set;
}
...
public void Send(ITelemetry item)
{
Items.Add(item);
}
}
...
MockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration configuration = new TelemetryConfiguration
{
TelemetryChannel = MockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
configuration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
TelemetryClient telemetryClient = new TelemetryClient(configuration);
container.Register<TelemetryClient>(telemetryClient);
Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors.
This TelemetryClient is a framework type and framework types should not be auto-wired by your container.
I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated.
Yep, this complexity is deliberate, because we want to discourage people from creating components with multiple constructors, because this is an anti-pattern.
Instead of using auto-wiring, you can, as #qujck already pointed out, simply make the following registration:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Very unlikely. If you want to test the class that depends on this TelemetryClient, you better use a fake implementation instead, to prevent your unit test to either become fragile, slow, or to pollute your Insight data. But even if testing isn't a concern, according to the Dependency Inversion Principle you should depend on (1) abstractions that are (2) defined by your own application. You fail both points when using the TelemetryClient.
What you should do instead is define one (or perhaps even multiple) abstractions over the TelemetryClient that are especially tailored for your application. So don't try to mimic the TelemetryClient's API with its possible 100 methods, but only define methods on the interface that your controller actually uses, and make them as simple as possible so you can make both the controller's code simpler -and- your unit tests simpler.
After you defined a good abstraction, you can create an adapter implementation that uses the TelemetryClient internally. I image you register this adapter as follows:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(new TelemetryClient(...)));
Here I assume that the TelemetryClient is thread-safe and can work as a singleton. Otherwise, you can do something like this:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
Here the adapter is still a singleton, but is provided with a delegate that allows creation of the TelemetryClient. Another option is to let the adapter create (and perhaps dispose) the TelemetryClient internally. That would perhaps make the registration even simpler:
container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter());
I had a lot of success with using Josh Rostad's article for writing my mock TelemetryChannel and injecting it into my tests. Here's the mock object:
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new ConcurrentBag<ITelemetry>();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
And then in my tests, a local method to spin-up the mock:
private TelemetryClient InitializeMockTelemetryChannel()
{
// Application Insights TelemetryClient doesn't have an interface (and is sealed)
// Spin -up our own homebrew mock object
MockTelemetryChannel mockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString(),
};
TelemetryClient mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
Finally, run the tests!
[TestMethod]
public void TestWidgetDoSomething()
{
//arrange
TelemetryClient mockTelemetryClient = this.InitializeMockTelemetryChannel();
MyWidget widget = new MyWidget(mockTelemetryClient);
//act
var result = widget.DoSomething();
//assert
Assert.IsTrue(result != null);
Assert.IsTrue(result.IsSuccess);
}
If you don't want to go down the abstraction / wrapper path. In your tests you could simply direct the AppInsights endpoint to a mock lightweight http server (which is trivial in ASP.NET Core).
appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
How to set up "TestServer" in ASP.NET Core http://josephwoodward.co.uk/2016/07/integration-testing-asp-net-core-middleware
Another option without going the abstraction route is to disable telemetry before doing running your tests:
TelemetryConfiguration.Active.DisableTelemetry = true;
Based on other work here;
Create the channel - you can use this for testing telemetries if needed
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
Use a nice little static factory class
public static class MockTelemetryClient
{
public static TelemetryClient Create()
{
var mockTelemetryChannel = new MockTelemetryChannel();
var mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
var mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
}
Call MockTelemetryClient.Create() to get your TelemetryClient
Profit
A colleague of mine wrote this useful library that introduces abstractions for some of these core telemetry types (e.g. ITelemetryClient and IMetric).
https://github.com/thomhurst/ApplicationInsights.TelemetryLogger
Very easy to implement. You'll barely have to change anything in your production code, and mocking in tests becomes a breeze. Here's an extract from the README:
Dependency Injection
Call AddApplicationInsightsTelemetry() as normal, and then call AddApplicationInsightsTelemetryClientInterfaces()
public void ConfigureServices(IServiceCollection services)
{
services
.AddApplicationInsightsTelemetry()
.AddApplicationInsightsTelemetryClientInterfaces();
}
ITelemetryClient
Want the same usage as TelemetryClient? Inject ITelemetryClient into your classes. It has all the available methods of TelemetryClient (apart from any methods which shouldn't be called. e.g. internal or deprecated).
public class MyClass
{
private readonly ITelemetryClient _telemetryClient;
public MyClass(ITelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void DoSomething()
{
_telemetryClient.TrackTrace("Something happened");
}
}

Mocking the httprequest with an manually defined accepttype or null [duplicate]

I have following web Api controller method.
When I run this code through web, HttpContext.Current is never null and give desired value.
public override void Post([FromBody]TestDTO model)
{
var request = HttpContext.Current.Request;
var testName = request.Headers.GetValues("OS Type")[0];
// more code
}
However, when I call this method from Unit Test, HttpContext.Current is always null.
How do i fix it?
During unit tests HttpContext is always null as it is usually populate by IIS. You have a few options around this.
Sure, you could mock the HttpContext, (which you shouldn't really do - Don't mock HttpContext!!!! He doesn't like to be mocked!),. You should really try to stay away from tight coupling with HttpContext all over your code. Try constraining it to one central area (SRP);
Instead figure out what is the functionality you would like to achieve and design an abstraction around that. This will allow for your code to be more testable as it is not so tightly coupled to HttpContext.
Based on your example you are looking to access header values. This is just an example of how to change your thinking when it comes to using HttpContext.
Your original example has this
var request = HttpContext.Current.Request;
var testName = request.Headers.GetValues("OS Type")[0];
When you are looking for something like this
var testName = myService.GetOsType();
Well then create a service that provides that
public interface IHeaderService {
string GetOsType();
}
which could have a concrete implementation like
public class MyHeaderService : IHeaderService {
public string GetOsType() {
var request = HttpContext.Current.Request;
var testName = request.Headers.GetValues("OS Type")[0];
return testName;
}
}
Now in your controller you can have your abstraction instead of having tight coupling to HttpContext
public class MyApiController : ApiController {
IHeaderService myservice;
public MyApiController(IHeaderService headers) {
myservice = headers;
}
public IHttpActionResult Post([FromBody]TestDTO model) {
var testName = myService.GetOsType();
// more code
}
}
You can later inject your concrete type to get the functionality you want.
For testing you then swap dependencies to run your test.
If the method under test is your Post() method you can create a fake dependency or use a mocking framework
[TestClass]
public class MyTestClass {
public class MyFakeHeaderService : IHeaderService {
string os;
public MyFakeHeaderService(string os) {
this.os = os;
}
public string GetOsType() {
return os;
}
}
[TestMethod]
public void TestPostMethod() {
//Arrange
IHeaderService headers = new MyFakeHeaderService("FAKE OS TYPE");
var sut = new MyApiController(headers);
var model = new TestDTO();
//Act
sut.Post(model);
//Assert
//.....
}
}
This is by design and it's always null. But there is a FakeHttpContext project on Nuget that simply you can use it.
To install FakeHttpContext, run the following command in the Package Manager Console (PMC)
Install-Package FakeHttpContext
And then use it like this:
using (new FakeHttpContext())
{
HttpContext.Current.Session["mySession"] = "This is a test";
}
Visit https://www.nuget.org/packages/FakeHttpContext to install the package
See examples on Github: https://github.com/vadimzozulya/FakeHttpContext#examples
Hope this will help :)
All you need is
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
From unit-testing-controllers-in-web-api

C# unit testing API 2 call

I have a web api 2 web service get method. Inside I'm using HttpContext.Current.Request.UserHostAddress. When calling my controller method directly int he unit test this isn't filled in so is errors with null object. So I searched for how to fill this in and found the following which helped with that issue: Add IP address to HttpRequestMessage
However, this needs a server name to send the request to. The problem is that when tests run the VSExpress will need to be running for this API web service, which it won't be when just running the tests. On top of that even if it was it seems it picks a random port to run on so I couldn't hardcode the address like he does in the above link. How can I test my api 2 method given the above issues?
This is the line that blows up when I just test the api method
string ip = HttpContext.Current.Request.UserHostAddress;
[EDIT] Answer
Just so everyone knows here is the solution in code
public class MyController : ApiController
{
private: HttpRequestBase httpRequest;
public MyController()
{
httpRequest = new HttpRequestWrapper(HttpContext.Current.Request)
}
public MyController(HttpRequestBase http)
{
httpRequest = http;
}
public HttpResponseMessage Get()
{
string ip = httpRequest.UserHostAddress;
}
}
I use Moq in the unit test:
Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>();
httpRequestMock.Setup(x => x.UserHostAddress).Returns("127.0.0.1");
// then pass httpRequestMock.Object to my controller ctor and good to go
Decouple your controller from the HTTP context. There might be some built-in functionality to do this with which I'm unfamiliar, but one approach would be to simply inject a mockable object. Consider something like this:
public interface IRequestInformation
{
string UserHostAddress { get; }
}
public class RequestInformation : IRequestInformation
{
public string UserHostAddress
{
get { return HttpContext.Current.Request.UserHostAddress; }
}
}
Now you've abstracted the dependency on HttpContext behind an interface. If you're using dependency injection, inject that interface into your controller. If you're not, you can fake it:
// in your controller...
private IRequestInformation _request;
public IRequestInformation RequestInfo
{
get
{
if (_request == null)
_request = new RequestInformation();
return _request;
}
set { _request = value; }
}
Then use that in your controller logic:
string ip = RequestInfo.UserHostAddress;
Now in your unit tests you can supply a mock/fake/etc. for the RequestInfo property. Either create one manually or use a mocking library. If you create one manually, that's simple enough:
public class RequestInformationFake : IRequestInformation
{
public string UserHostAddress
{
get { return "some known value"; }
}
}
Then just supply that to the controller when arranging the test:
var controller = new YourController();
controller.RequestInformation = new RequestInformationFake();
// run your test
Replace your references to HttpContext by references to HttpContextBase. When in your code, initialize the HttpContextBase with a HttpContextWrapper instance, which is a the default behavior implementation in a web stack.
However in your test inject a custom HttpContextBase implementation where you implement the methods and behaviors needed by your test only.
As precised in the link:
The HttpContextBase class is an abstract class that contains the same
members as the HttpContext class. The HttpContextBase class enables
you to create derived classes that are like the HttpContext class, but
that you can customize and that work outside the ASP.NET pipeline.
When you perform unit testing, you typically use a derived class to
implement members with customized behavior that fulfills the scenario
you are testing.
Add the following method to the controller, or inject the equivalent. It uses the magic string MS_HttpContext because that's what the AspNetWebStack implementation uses for exactly the same purpose.
HttpContextBase HttpContextBase => HttpContext.Current != null
? new HttpContextWrapper(HttpContext.Current)
: (HttpContextBase)Request.Properties["MS_HttpContext"]
Replace all other uses of HttpContext.Current in the controller with HttpContextBase.
When unit testing:
var context = new Mock<HttpContextBase>();
...
controller.Request = new HttpRequestMessage();
controller.Request.Properties["MS_HttpContext"] = context.Object;

Unit Testing with DbContext mock through Service Layer

I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.
I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.
namespace ChatProj.Service_Layer
{
public class UserService : IUserService
{
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
private IMessageRepository _messageRepository;
-> public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:
public void Save()
{
context.SaveChanges();
}
This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).
Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:
namespace ChatProj.Tests
{
[TestFixture]
class MessageRepositoryTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void SaveMessage_SaveWorking_VerifyUse()
{
//Arrange
var userServiceMock = new Mock<UserService>();
var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
var repositoryMock = new Mock<IMessageRepository>();
var contextMock = new Mock<MessageContext>();
MessageRepository messageRepository = new MessageRepository(contextMock.Object);
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
//Assert
repositoryMock.Verify(m => m.Save());
userServiceMock.Verify(m => m.SaveMessage(message));
}
}
I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.
So I'm wondering, how do I practically get my Unit Test to work?
You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method.
Right now it still tries to access a real DB.
But you can setup only virtual properties or if it will be an inteface.
So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.
Take a look at these two lines:
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.
public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.

Categories

Resources