Mock testing of tweets received and responded to - c#

I have two interfaces called ITweetReq and ITweetResp.
An outside service sends Requests to send tweets when they come in, which is implemented by the interface ITweetReq and our system in return sends the reply back which is implemented in ITweetResp interface.
Both the incoming and outgoing messages are logged in a file. I have to unit test the log file to make sure it is logging the correct number of message received and replied.
I am wondering if I should mock both the interfaces then do a test on the log file, but not sure how to go about it. Any help or suggestion will be appreciated.
I am using NUnit and Moq Framework.
var TweetReq = new Mock<ITweetReq>();
var TweetRes = new Mock<ITweetRes>();

You seem a little confused as to what mocking/unit testing is.
Mocks are typically created for anything 'outside' of the class being tested.
Unit tests are run on your code (typically a method, or part of it).
You should write unit tests for the class which does the logging, by calling the methods which receive/reply to tweets with the mocked objects, and testing for the expected result.
It might be better if you mocked your logging class too, as testing files directly can become complicated and is not recommended.
For example, you might have these methods on your class.
public class TweetHandler
{
public void Main()
{
ILogger logger = GetLogger();
while (true)
{
ITweetReq request = GetNextRequest();
ITweetRes response = HandleRequest(request, logger);
SendResponse(response, logger);
}
}
public ITweetRes HandleRequest(ITweetReq request, ILogger logger)
{
int id = request.Id;
string text = request.Text;
logger.Log("Received tweet " + id + " with text: " + text);
return new TweetResponse(id, text);
}
public void SendResponse(ITweetRes response, ILogger logger)
{
logger.Log("Sending response to tweet: " + response.Id);
Response(reponse);
}
}
You can then write unit tests like this:
[TestMethod]
public void HandleRequest_ShouldLogIdAndText()
{
// Arrange
var handler = new TweetHandler();
var mockRequest = new Mock<ITweetReq>();
var mockLogger = new Mock<ILogger>();
mockRequest.Setup(x => x.Id).Returns(10);
mockRequest.Setup(x => x.Text).Returns("some text");
// Act
handler.HandleRequest(mockRequest, mockLogger);
// Assert
mockLogger.Verify(x => x.Log("Received tweet 10 with text: some text"));
}
[TestMethod]
public void SendResponse_ShouldLogId()
{
// Arrange
var handler = new TweetHandler();
var mockResponse = new Mock<ITweetRes>();
var mockLogger = new Mock<ILogger>();
mockResponse.Setup(x => x.Id).Returns(20);
// Act
handler.SendReponse(mockResponse, mockLogger);
// Assert
mockLogger.Verify(x => x.Log("Sending response to tweet: 20"));
}

Related

Moq checking if method is called with parameter from another call

I have the following function that I am trying to put under unit test
public MessageListDto GetMessageList(SimpleSearchCriteria criteria)
{
var messages = _repository.GetMessages(criteria, out int total);
return new MessageListDto(messages, total);
}
and the following is my test so far, in which I am able to determine that _repository.GetMessages is call with the correct parameters.
However, how do I test that the second line is tested properly, I need to test that
A new object of type MessageListDto is constructed with the two parameters returned from the line above
the newly constructed object is returned
[Test]
public void Test1()
{
int total = 10;
var searchCriteria = new SimpleSearchCriteria();
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);
messageService.GetMessageList(searchCriteria);
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
mockRepo.Verity ??????????
}
As we have discussed in the comment section your goal is to test the returned messages is passed to the dto's constructor without any modification.
With your current code you can't do that because you are creating the DTO explicitly inside your method. So, you can't replace it with a spy.
Just to clarify some terminologies:
Dummy: simple code that returns bogus data
Fake: a working alternative which can take shortcuts
Stub: custom logic with predefined data
Mock: custom logic with expectations (interactive stub)
Shim: custom logic at run-time
Spy: interceptors to record calls
To be able to capture the arguments of the MessageListDto constructor call you need to introduce an other layer between your GetMessageList method and the constructor. (In other words indirection)
For the sake of simplicity let me assume that IMessageRepository's GetMessages returns a string array.
So, you can introduce an interface like this:
public interface IDtoProvider
{
public MessageListDto CreateMessageList(string[] messages, int total)
=> new MessageListDto(messages, total);
}
Here I have used C# 8's interface default implementation feature
It can be further shortened with C# 9's target-typed new expression feature
If you are using older C# version then you need to separate the interface and the default implementation from each other
Now lets amend your MessageService to receive a IDtoProvider instance via its constructor
class MessageService
{
private readonly IMessageRepository _repository;
private readonly IDtoProvider _dtoProvider;
public MessageService(IMessageRepository repository, IDtoProvider dtoProvider)
=> (_repository, _dtoProvider) = (repository, dtoProvider);
public MessageListDto GetMessageList(SimpleSearchCriteria criteria)
{
var messages = _repository.GetMessages(criteria, out int total);
return _dtoProvider.CreateMessageList(messages, total);
}
...
}
Here I've took advantage of ValueTuple's deconstruct capability
With these in our hand we can write a unit test like this:
//Arrange - Repo
string[] messages = new[] { "OnlyElement" };
int total = 10;
var mockRepo = new Mock<IMessageRepository>();
mockRepo
.Setup(repo => repo.GetMessages(It.IsAny<SimpleSearchCriteria>(), out total))
.Returns(messages);
//Arrange - Provider
var dto = new MessageListDto(messages, total);
string[] messagesArgument = null;
int totalArgument = -1;
var mockProvider = new Mock<IDtoProvider>();
mockProvider
.Setup(provider => provider.CreateMessageList(It.IsAny<string[]>(), It.IsAny<int>()))
.Callback<string[], int>((messages, total) => (messagesArgument, totalArgument) = (messages, total))
.Returns(dto);
//Arrange - SUT
var searchCriteria = new SimpleSearchCriteria();
var sut = new MessageService(mockRepo.Object, mockProvider.Object);
//Act
sut.GetMessageList(searchCriteria);
//Assert - Repo
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
//Assert - Provider
Assert.Equal(messages, messagesArgument);
Assert.Equal(total, totalArgument);
I've defined how should the IMessageRepository mock behave
I've defined how should the IDtoProvider mock behave
2.1 I've used here the Callback method to capture the calling argument
2.2 If you perform multiple calls against your mocked method then please consider to use Capture.In
I've instantiated a MessageService (which is our system under test) with the mock objects
I've called the GetMessageList inside the Act phase
I've made assertion against the repo mock call
I've made assertion against the calling parameters of the CreateMessageList
6.1 Here I have used xunit's assertion
The first step is to setup the MessageService mock, so that it returns something deterministic and then in the second step you verify that that has been used to construct your MessageListDto.
[Test]
public void Test1()
{
// arrange
int total = 10;
var searchCriteria = new SimpleSearchCriteria();
var messages = new [] {"message1", "message2"} // or similar
var mockRepo = new Mock<IMessageRepository>();
// or similar, I am not quite certain as to the specific syntax. Especially wrt. out parameters. Check the documentation.
mockRepo.Setup(x => x.GetMessages(It.IsAny<SimpleSearchCriteria>(), It.IsAny<int>())).Returns(messages);
var messageService = new MessageService(mockRepo.Object);
// act
var result = messageService.GetMessageList(searchCriteria);
// assert
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
// or similar; here you might want to check out FluentAssertions as #Peter Csala suggested
Assert.Equal(result.Messages, messages);
}
What's missing from previous answers is that whenever possible, you should set up the mocked method with the exact parameters, that you expect it to receive. In other words set up the method with concrete values instead of It.IsAny<>(). In such case you won't have to verify the method later at all. (Unless it's critical to test, how many times a method is called). Simply, if mocked GetMessages receives different arguments than expected, it will return null, set total to 0 and your test will fail. Having the mocked methods set up properly, you can now focus on verifying what GetMessageList returns, which is the purpose of this unit test.
[Test]
public void Test1()
{
int total = 10;
var messages = new[] {new Message()};
var searchCriteria = new SimpleSearchCriteria();
var mockRepo = new Mock<IMessageRepository>();
mockRepo.Setup(_ => _.GetMessages(searchCriteria, out total))
.Returns(messages);
var messageService = new MessageService(mockRepo.Object);
var dto = messageService.GetMessageList(searchCriteria);
Assert.AreSame(messages, dto.Messages);
Assert.AreEqual(total, dto.Total);
}

Unit test result in null object while testing a web api 2

I am new to MS Unit Testing and Moq objects. I am trying to test my Web API 2 controller. I have given below my unit test and controller code. While stepping through the code, it doesn't even go to the GetDeliveryCodeStrategy method.
[TestMethod]
public void CreateDelivery_ShouldReturnDeliveryCode()
{
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>
();
Mock<IDeliveryCode> deliveryCode = new Mock<IDeliveryCode>();
var controller = new DeliveryCodeController(deliveryStrategy.Object,
deliveryCode.Object);
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("appToken", "a57ffa87-950e-40f4-b965-17788becac7d");
controllerContext.Request = request;
controller.ControllerContext = controllerContext;
var result = controller.CreateDelivery(50) as
CreatedNegotiatedContentResult<IDeliveryCode>;
Assert.IsNotNull(result);
}
public class DeliveryCodeController : ApiController
{
IDeliveryStrategy _deliveryBatch;
IDeliveryCode _deliveryCode;
//Constructor dependency injection through Autofac
public DeliveryCodeController(IDeliveryStrategy DeliveryBatch,
IDeliveryCode deliveryCode)
{
_deliveryBatch = DeliveryBatch;
_deliveryCode = deliveryCode;
}
[HttpPost]
[Route("api/DeliveryCode/{percentage}")]
public IHttpActionResult CreateDelivery(int percentage)
{
String appToken = String.Empty;
if (Request.Headers.TryGetValues("appToken", out IEnumerable<String>
headerValues))
{
appToken = headerValues.FirstOrDefault();
}
if (!String.IsNullOrEmpty(appToken)))
{
IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
_deliveryCode.Code = deliveryContext.Create();
return Created(Request.RequestUri.ToString(), _deliveryCode);
}
else
{
return Content(HttpStatusCode.Forbidden, new Error { message = "The App
Token is not valid." });
}
}
}
When I do the "Debug Test" and step through the code, the deliveryContext
object comes as null in the code IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
You have to set up the Mock to return a certain value:
IDeliveryContext deliveryContext = // ???? - whatever you want it to be.
// Could be another Mock.
// This is what the Mock will return.
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<decimal>()))
.Returns(deliveryContext);
This tells the Mock that that when its GetDeliveryCodeStrategy method is called, it should return the specified IDeliveryContext. Depending on what you're trying to do, that could be another Mock. (Mocks that return mocks are undesirable, but if you're starting out I'd file that detail away and come back to it.)
I'm guessing that percentage is a decimal. It.IsAny<decimal>() means that the mock doesn't care what the value is. That's usually okay because what you're testing is what your class does with the object returned by the mock.
You need to call Setup() on mock objects for the methods that you want to use:
var deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<int>))
.Returns(AMockOfDeliveryContext); //you need to mock it beforehand so you can
//use the object here

Testing HttpModule wtih Response.Filter and Response.Write

I'm working on an Http Module that simply records the response time and size and then appends the results to the response body.
My module looks something like this:
public override void PreRequestHandlerExecute(HttpContextBase context)
{
// Add a filter to capture response stream
context.Response.Filter = new ResponseSniffer(context.Response.Filter);
}
public override void ApplicationEndRequest(HttpContextBase context)
{
....
context.Response.Write(builder.ToString());
}
I now wish to unit test this module. I'm very new to unit testing. I've adapted code from o2platform to get a moq httpcontext and that works so far. However, the response filter does seem to get set in Pre and the response body is what I initialized it has from the test setup.
I've tried a handful options (and read a lot of stuff) but none of these seemed to work:
public Mock<HttpResponseBase> MockResponse { get; set; }
...
var outputStream = new MemoryStream();
var filter = new MemoryStream();
//MockResponse.Setup(response => response.OutputStream).Returns(GetMockStream(outputStream).Object);
//MockResponse.Setup(response => response.Filter).Returns(GetMockStream(filter).Object);
MockResponse.Setup(response => response.OutputStream).Returns(() => outputStream);
//MockResponse.SetupSet(response => response.OutputStream = It.IsAny<Stream>()).Returns(() => outputStream);
MockResponse.Setup(response => response.Filter).Returns(() => filter);
MockResponse.SetupSet(response => response.Filter = It.IsAny<Stream>());
MockResponse.SetupSet(response => response.Filter = It.IsAny<ResponseSniffer>());
Test Method
[TestMethod]
public void TestMethod1()
{
var mockHttpContext = new MoqHttpContext();
var httpContext = mockHttpContext.HttpContext();
var html = #"<html>
<head></head>
<body>
<h1>Hello World</h1>
</body>
</html>";
httpContext.ResponseWrite(html);
httpContext.StreamWrite(httpContext.Response.Filter, html);
var module = new Module();
module.PreRequestHandlerExecute(mockHttpContext.HttpContext());
module.ApplicationBeginRequest(mockHttpContext.HttpContext());
module.ApplicationEndRequest(mockHttpContext.HttpContext());
var responseRead = httpContext.ResponseRead(); //extension method to get output stream
var b = 1; //put breakpoint here
}
I realize the test needs to Assert rather then the breakpoint. I also realize that the test should be broken up a bit.
Code Repo
Github
Let's see at the following statement in Module.ApplicationEndRequest() method:
context.Response.Write(builder.ToString());
When this code is executed from Unit Test, context.Response is a mock that you set up in MoqHttpContext.CreateBaseMocks():
MockResponse = new Mock<HttpResponseBase>();
// ...
MockContext.Setup(ctx => ctx.Response).Returns(MockResponse.Object);
You can't expect that you call a Write() method on a mock and then can read the same data back. Mock is a fake object. Its default implementation of Write() method does nothing, and passed data is just lost.
To fix the problem, you could setup a callback on Response mock that will write passed data to a stream and then return it back on read. You are actually very close to it.
In MoqHttpContext class declare a stream where you will save the data:
public class MoqHttpContext
{
private readonly MemoryStream _outputStream = new MemoryStream();
// ...
}
Then in CreateBaseMocks() method setup a callback:
public MoqHttpContext CreateBaseMocks()
{
// ...
MockResponse = new Mock<HttpResponseBase>();
MockResponse.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(s =>
{
var data = Encoding.ASCII.GetBytes(s);
_outputStream.Write(data, 0, data.Length);
_outputStream.Flush();
_outputStream.Position = 0;
});
// ...
}
You also should remove a line that sets inputStream position to 0 in MoqHttpContextExtensions.StreamWrite(), so that html data that you write in UnitTest1.TestMethod1() is appended, not overwritten:
public static HttpContextBase StreamWrite(this HttpContextBase httpContextBase, Stream inputStream, string text)
{
if (inputStream == null) inputStream = new MemoryStream();
var streamWriter = new StreamWriter(inputStream);
inputStream.Position = inputStream.Length;
streamWriter.Write(text);
streamWriter.Flush();
// Remove this line
//inputStream.Position = 0;
return httpContextBase;
}
That's it. Now if you check value of responseRead in the test, you will see that data appended by Http module is there.
UPDATE (Fixing problem with a filter)
There are 3 different issues with current code that prevent correct work of a filter from UT.
You tried handful options for mocking Filter property, however none of them seems correct. The correct way to mock property getter with Moq is:
MockResponse.SetupGet(response => response.Filter).Returns(filter);
Remove all other statements for mocking response.Filter, but don't add above statement yet, it's not a final version.
You have following check in Module.ApplicationEndRequest() method:
if (context.Response.Filter is ResponseSniffer filter)
{
// ...
When UT is executed, context.Response.Filter is a MemoryStream not a ResponseSniffer. Setter that is called in Module constructor:
context.Response.Filter = new ResponseSniffer(context.Response.Filter);
will not actually affect value returned by Filter getter because it's a mock that currently always return instance of MemoryStream that you setup with SetupGet. To fix this problem you should actually emulate property behavior: save the value passed to setter and return it in the getter. Here is a final setup of response.Filter property:
Stream filter = new MemoryStream();
MockResponse.SetupSet(response => response.Filter = It.IsAny<Stream>()).Callback<Stream>(value => filter = value);
MockResponse.SetupGet(response => response.Filter).Returns(() => filter);
Make sure you have deleted all other mocks of response.Filter property.
The final problem that you should fix - is the order of Module invocations from UT. Currently the order is the following:
httpContext.StreamWrite(httpContext.Response.Filter, html);
// ...
var module = new Module();
module.PreRequestHandlerExecute(mockHttpContext.HttpContext());
But PreRequestHandlerExecute sets Response.Filter with an instance of ResponseSniffer. So when httpContext.StreamWrite above it is called, httpContext.Response.Filter holds actually instance of MemoryStream, not ResponseSniffer. So the last fix you should make is to change the order of statements in UT body:
// ...
var module = new Module();
module.PreRequestHandlerExecute(mockHttpContext.HttpContext());
httpContext.ResponseWrite(html);
httpContext.StreamWrite(httpContext.Response.Filter, html);
module.ApplicationBeginRequest(mockHttpContext.HttpContext());
module.ApplicationEndRequest(mockHttpContext.HttpContext());
// ...
UPDATE (UT Redesign)
At this point your UT should work. However current test is very cumbersome. The fact that it takes so much time to understand why it does not work proves it. Such tests are very hard to maintain and fix, they become a real pain over time.
Moreover it's rather Integration test than Unit test, because it invokes several of classes with different functionality - ResponseSniffer and Module.
You should strongly consider redesign of current test. And the good start is to make separate tests for ResponseSniffer and Module classes.
Most valuable test for ResponseSniffer is the one that verifies that written data is registered in RecordStream:
[TestClass]
public class ResponseSnifferTests
{
[TestMethod]
public void Write_WritesDataToRecordStream()
{
// Arrange
var inData = new byte[] { 0x01 };
var target = new ResponseSniffer(Mock.Of<Stream>());
// Act
target.Write(inData, 0, inData.Length);
// Assert
target.RecordStream.Position = 0;
var outData = new byte[inData.Length];
int outSize = target.RecordStream.Read(outData, 0, outData.Length);
Assert.AreEqual(inData.Length, outSize);
CollectionAssert.AreEqual(inData, outData);
}
}
As regards Module class, there are several checks that should be made:
PreRequestHandlerExecute() sets Response.Filter with instance of ResponseSniffer.
ApplicationBeginRequest() adds Stopwatch to context.Items dictionary.
ApplicationEndRequest() writes request info to the response.
UT approach implies checking of these facts in separate tests. Here are samples of such 3 tests:
[TestClass]
public class ModuleTests
{
[TestMethod]
public void PreRequestHandlerExecuteShouldSetResponseSnifferAsFilter()
{
// Arrange
Stream filter = null;
Mock<HttpResponseBase> httpResponseMock = new Mock<HttpResponseBase>();
httpResponseMock.SetupSet(response => response.Filter = It.IsAny<Stream>()).Callback<Stream>(value => filter = value);
Mock<HttpContextBase> httpContextStub = new Mock<HttpContextBase>();
httpContextStub.SetupGet(x => x.Response).Returns(httpResponseMock.Object);
var target = new Module();
// Act
target.PreRequestHandlerExecute(httpContextStub.Object);
// Assert
Assert.IsNotNull(filter);
Assert.IsInstanceOfType(filter, typeof(ResponseSniffer));
}
[TestMethod]
public void ApplicationBeginRequestShouldStoreStopwatchInContextItems()
{
// Arrange
var items = new Dictionary<string, object>();
Mock<HttpContextBase> httpContextStub = new Mock<HttpContextBase>();
httpContextStub.SetupGet(x => x.Items).Returns(items);
var target = new Module();
// Act
target.ApplicationBeginRequest(httpContextStub.Object);
// Assert
Assert.IsTrue(items.ContainsKey("X-ResponseTime"));
Assert.IsInstanceOfType(items["X-ResponseTime"], typeof(Stopwatch));
}
[TestMethod]
public void ApplicationEndRequestShouldAddRequestInfoToResponse()
{
// Arrange
Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>();
httpRequestMock.SetupGet(x => x.FilePath).Returns("/test");
string writtenData = null;
Mock<HttpResponseBase> httpResponseMock = new Mock<HttpResponseBase>();
httpResponseMock.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(s => writtenData = s);
Mock<HttpContextBase> httpContextStub = new Mock<HttpContextBase>();
httpContextStub.SetupGet(x => x.Request).Returns(httpRequestMock.Object);
httpContextStub.SetupGet(x => x.Response).Returns(httpResponseMock.Object);
httpContextStub.SetupGet(x => x.Items).Returns(new Dictionary<string, object> { ["X-ResponseTime"] = new Stopwatch() });
var target = new Module();
// Act
target.ApplicationEndRequest(httpContextStub.Object);
// Assert
Assert.IsTrue(Regex.IsMatch(writtenData, #"Response Size: \d+ bytes<br/>"));
Assert.IsTrue(Regex.IsMatch(writtenData, #"Module request time: \d+ ms"));
}
}
As you see, the tests are pretty simple and straightforward. You don't need those MoqHttpContext and MoqHttpContextExtensions with a lot of mocks and helpers anymore. Another benefit - if some of the tests get broken, it's much easier to identify the root cause and fix it.
If you are new to Unit Testing and are looking for good source of info on it, I strongly suggest book The Art of Unit Testing by Roy Osherove.

.NET Core how to unit test service?

I have build a WebAPI and want to create a unit test project to have my services tested automatically.
The flow of my WebAPI is simple:
Controller (DI Service) -> Service (DI Repository) -> _repo CRUD
Suppose I have a service like:
public int Cancel(string id) //change status filed to 'n'
{
var item = _repo.Find(id);
item.status = "n";
_repo.Update(item);
return _repo.SaveChanges();
}
And I want to build a unit test, which just use InMemoryDatabase.
public void Cancel_StatusShouldBeN() //Testing Cancel() method of a service
{
_service.Insert(item);
int rs = _service.Cancel(item.Id);
Assert.Equal(1, rs);
item = _service.GetByid(item.Id);
Assert.Equal("n", item.status);
}
I've searched other related question, found that
You can't use dependency injections on test classes.
I just want to know if there is any other solution to achive my unit test idea?
When unit testing, you should just supply all the dependencies of the class you are testing explicitly. That is dependency injection; not having the service construct its dependencies on its own but making it rely on the outer component to provide them. When you are outside of a dependency injection container and inside a unit test where you are manually creating the class you are testing, it’s your responsibility to provide the dependencies.
In practice, this means that you either provide mocks or actual objects to the constructor. For example, you might want to provide a real logger but without a target, a real database context with a connected in-memory database, or some mocked service.
Let’s assume for this example, that the service you are testing looks like this:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
So in order to test ExampleService, we need to provide those three objects. In this case, we will do the following for each:
ILogger<ExampleService> – we will use a real logger, without any attached target. So any call on the logger will work properly without us having to provide some mock, but we do not need to test the log output, so we do not need a real target
MyDbContext – Here, we’ll use the real database context with an attached in-memory database
UtilityService – For this, we will create a mock which just setups the utility method we need inside the methods we want to test.
So a unit test could look like this:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
In your specific Cancel case, you want to avoid using any methods of the service you are not currently testing. So if you want to test Cancel, the only method you should call from your service is Cancel. A test could look like this (just guessing the dependencies here):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw. note that I’m opening up a new database context all the time in order to avoid getting results from the cached entities. By opening a new context, I can verify that the changes actually made it into the database completely.

Integration test good practice

I am using unit test for some time. But now I need to write integration test. I should save something to DB, and than check if saved data is ok.
I can't find simple clean example of integration test.
My idea is to do like this:
[Test]
public void IntegrationTestExample()
{
// Arrange without mocking
var objec1 = new objec1();
var objec2 = new objec2();
var startTestClass = new startTestClass(objec1, objec2);
var saveData = "test data";
//Act
startTestClass.SaveToDB(saveData);
// Assert
var valueFromDB = SelectSavedData();
Assert.AreEqual(saveData, valueFromDB);
}
//Get data from DB for assert
private string SelectSavedData()
{
var sqlQuery = "Select TOP 1 data from table1";
var data = RepositoryForTest.selectSavedData(sqlQuery);
return data;
}
But I am not sure if this is good approach?
Do you have any suggestions?
You should test one piece of integration at a time, and leave the DB in such a state that it can run multiple tests and they should not affect each other. The MS test framework allows you to define methods that will setup and cleanup the tests at certain times during the test run, as shown in the following code
[TestClass]
public class ReturnServiceTest {
[ClassInitialize()]
public static void ClassInit(TestContext context) {
//INIT TEST DATA FOR THE CLASS
}
[TestCleanup]
public void TestCleanup() {
//CLEANUP DATA AFTER EACH TEST
}
[TestMethod]
public void IntegrationTestExample() {
// Arrange without mocking
var objec1 = new objec1();
var objec2 = new objec2();
var startTestClass = new startTestClass(objec1, objec2);
var saveData = "test data";
//Act
startTestClass.SaveToDB(saveData);
// Assert
var valueFromDB = SelectSavedData();
Assert.AreEqual(saveData, valueFromDB);
}
//Get data from DB for assert
private string SelectSavedData() {
var sqlQuery = "Select TOP 1 data from table1";
var data = RepositoryForTest.selectSavedData(sqlQuery);
return data;
}
}
When you are using a real database you should clean it up after each test attempt.
How you access the data is also not very clear, what proves, that RepositoryForTest does not contain any errors? Try to do the simplest thing that is possible for your assertion.
My Tests against databases look like:
public void Test()
{
try
{
// Fixture setup
// Create and insert data into Database (with plain ADO code)
// Exercise system
// Verify outcome
// use your data layer here
}
finally
{
// Teardown - other methods to clean the tables afterwards
DatabaseHelper.ClearLookups();
DatabaseHelper.ClearBeds();
...
}
}

Categories