Edit: Context: I write a Silverlight application that accesses a Dynamics CRM 2011 via the Soap Service. I implemented a service for doing this. I want to write a unit test for this service now.
I want to write a unit test for the following method:
public async Task<List<string>> GetAttributeNamesOfEntity(string entityName)
{
// build request
OrganizationRequest request = new OrganizationRequest
{
RequestName = "RetrieveEntity",
Parameters = new ParameterCollection
{
new XrmSoap.KeyValuePair<string, object>()
{
Key = "EntityFilters",
Value = EntityFilters.Attributes
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "RetrieveAsIfPublished",
Value = true
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "LogicalName",
Value = "avobase_tradeorder"
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "MetadataId",
Value = new Guid("00000000-0000-0000-0000-000000000000")
}
}
};
// fire request
IAsyncResult result = OrganizationService.BeginExecute(request, null, OrganizationService);
// wait for response
TaskFactory<OrganizationResponse> tf = new TaskFactory<OrganizationResponse>();
OrganizationResponse response = await tf.FromAsync(result, iar => OrganizationService.EndExecute(result));
// parse response
EntityMetadata entities = (EntityMetadata)response["EntityMetadata"];
return entities.Attributes.Select(attr => attr.LogicalName).ToList();
}
My first approach was to call the actual CRM. This failed because I cannot authenticate. I asked a question about this here. My second approach is to mock the organization service and run the method against my the mock. Like this:
private bool _callbackCalled = false;
private SilverlightDataService _service;
private List<string> names;
[TestInitialize]
public void SetUp()
{
IOrganizationService organizationService = A.Fake<IOrganizationService>();
_service = new SilverlightDataService {OrganizationService = organizationService};
IAsyncResult result = A.Fake<IAsyncResult>();
A.CallTo(organizationService.BeginExecute(A<OrganizationRequest>.Ignored, A<AsyncCallback>.Ignored,
A<object>.Ignored)).WithReturnType<IAsyncResult>().Returns(result);
EntityMetadata entities = new EntityMetadata();
AttributeMetadata meta = new AttributeMetadata();
meta.LogicalName = "foobar";
entities.Attributes = new ObservableCollection<AttributeMetadata>();
entities.Attributes.Add(meta);
OrganizationResponse response = new OrganizationResponse();
response.Results = new ParameterCollection();
response["EntityMetadata"] = entities;
A.CallTo(() => result.IsCompleted).Returns(true);
A.CallTo(result.AsyncState).WithReturnType<object>().Returns(response);
A.CallTo(organizationService.EndExecute(result)).WithReturnType<OrganizationResponse>().Returns(response);
}
[TestMethod]
[Asynchronous]
public async void TestGetAttributeNamesOfEntity()
{
TaskFactory<List<string>> tf = new TaskFactory<List<string>>();
names = await tf.FromAsync(_service.GetAttributeNamesOfEntity("avobase_tradeorder"), CallbackFunction);
Assert.IsTrue(_callbackCalled);
Assert.IsNotNull(names);
}
/// <summary>
/// is used as callback for the method GetAttributeNamesOfEntity
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
private List<string> CallbackFunction(IAsyncResult result)
{
_callbackCalled = true;
names = (List<string>) result.AsyncState;
return null;
}
I use FakeItEasy for mocking. When I execute this test (with ReSharper and AgUnit) it waits forever. I also tried this approach, but it did not work.
1) My assumption is that my mocks dont tell the await magic that the task is actually done. Why does the await never end? What do I have to do to fix this?
2) Is there maybe a better approach to do this?
Edit: I am using: VS 2012 Premium, ReSharper 7.1, Silverlight 5, AgUnit 0.7, .Net 4.5, FakeItEasy 1.13.1 installed via NuGet.
Related
I am trying to get the data from an api source and seed the data it to my database using efCore.
/// <summary>
/// Get the games from the rawg api and parse them into the game domain entity
/// </summary>
public static async Task<List<Game>> ApiRawgGamesRequest()
{
var gamesList = new List<Game>();
for (int i = 1; i < 250; i++)
{
using (var msg = new HttpRequestMessage(HttpMethod.Get, new Uri($"https://api.rawg.io/api/games?page_size=40&page=1")))
using (var response = await _client.SendAsync(msg))
{
response.EnsureSuccessStatusCode();
var gamesResponse = await response.Content.ReadAsAsync<GameResponse>();
gamesList.AddRange(gamesResponse.Results);
}
}
return gamesList;
}
then this is the code to seed the data into my database using ef core
/// <summary>
/// Add the games gotten from the api into the games table in the db
/// </summary>
public static async Task SeedGamesData(DataContext context)
{
var responseFromRawg = await ApiRawgGamesRequest();
await context.Games.AddRangeAsync(responseFromRawg);
await context.SaveChangesAsync();
And i also wrote some code to migrate the database and seed as soon as the api starts up
public static void Main(string[] args)
{
// migrating the database on start
var host = CreateHostBuilder(args).Build();
using (var service = host.Services.CreateScope())
{
var services = service.ServiceProvider;
try
{
var context = services.GetRequiredService<DataContext>();
context.Database.Migrate();
SeedGames.SeedGamesData(context);
}
catch (Exception e)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(e, "There was an error during migration");
}
}
host.Run();
}
But then i dont get anything or any confirmation that the code has been carried out or that i got an error. I get no errors, no warnings, nothing!. and the data still doesnt show up in my database.
is there a better way to go about adding data from an external api into your database?
I'm currently setting up some unit tests to return ID of the cafes using some mocking, but for some reason the result variable always returns null.
Setup in service class which implements the Get method
public async Task<CafeResponse> Get(int cafeId)
{
var cafe= await _cafeRepository.GetByIdAsync(cafeId);
return _mapper.Map<CafeResponse>(cafe);
}
Unit test setup: Currently the result variable shows null? Not sure if this is due to me setting up the mock incorrectly
[Fact]
public async Task Get_ShouldReturnCafeArray_WhenCafesInDatabase()
{
//Arrange
var cafeId = 98;
var cafeName = "bellas";
var cafeIdGuid = Guid.NewGuid();
var cafeDesc = "this is my test";
var cafeTypeId = 1;
var cafeDto = new Cafe
{
CafeId = cafeId,
Name = cafeName,
CafeGuid = cafeIdGuid,
Description = cafeDesc,
CafeTypeId = cafeTypeId,
};
var expected = new CafeResponse();
var mockCafe = new Mock<IRepositoryAsync<Cafe>>();
mockCafe.Setup(x => x.GetByIdAsync(cafeId)).ReturnsAsync(cafeDto);
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<Cafe, CafeResponse>(It.IsAny<Cafe>())).Returns(expected);
//Act
var cafeService = new CafeService(mockCafe.Object, mockMapper.Object);
var result = await cafeService.Get(cafeId); //always returns null
//Assert
mockCafe.Verify(x => x.GetByIdAsync(cafeId), Times.Once);
Assert.Equal(cafeId, result.CafeId);
}
The reason that you're getting a null result with the code that you have is because for IMapper you're mocking:
TDestination Map<TSource, TDestination>(TSource source);
but your actual code is using:
TDestination Map<TDestination>(object source);
So if you just want the test to return your "expected" instance of CafeResponse, then you need to update your Mock<IMapper> setup to be:
mockMapper.Setup(x => x.Map<CafeResponse>(It.IsAny<Cafe>())).Returns(expected);
The more appropriate solution as pointed out in the comments would be to simply create a Mapper instance for your test:
var mapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MyProfile>();
}).CreateMapper();
Otherwise, you're just testing that you're mock is returning what you tell it to and not really verifying any functionality in your code.
I am unit testing an application I made with an in-memory database using Entity Framework Core but I am running into a problem with one of my functions. I have so far tested getting a game by Id (it works), adding a game to the collection (it works too) and updating a game in the collection (it doesn't work).
The function code:
[TestMethod()]
public async Task UpdateGameAsyncTest()
{
//Arrange
await CreateDb();
GameDto gameDto = new GameDto()
{
Id = 2,
Team1 = "PSG",
Team2 = "AZ",
Date = DateTime.UtcNow
};
//Act
var id = await _gameService2.UpdateGameAsync(gameDto);
var game = await _gameService2.GetGameAsync(id);
//Assert
Assert.AreEqual("AZ", game.Team1);
}
UpdateGameAsync() in my service layer:
public async Task<int> UpdateGameAsync(GameDto game)
{
var gameEntity = game.ToEntity();
return await _gameRepository.UpdateGameAsync(gameEntity);
}
UpdateGameAsync() in my repository layer:
public async Task<int> UpdateGameAsync(Game game)
{
if (game.Result != null)
{
if (game.Result.Length == 3 && game.Result.Substring(1, 1) == "-")
{
game.typeResult = await CheckTypeResult(game.Result);
}
}
_dbContext.Games.Update(game);
return await AutoSaveChangesAsync();
}
CreateDb():
public async Task CreateDb()
{
var options = new DbContextOptionsBuilder<StrikeNetDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.EnableSensitiveDataLogging()
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.Options;
var dbContext = new StrikeNetDbContext(options);
if (await dbContext.Games.CountAsync() <= 0)
{
Game g1 = new Game()
{
Id = 1,
Team1 = "FCB",
Team2 = "BVB",
Date = DateTime.UtcNow
};
Game g2 = new Game()
{
Id = 2,
Team1 = "PSG",
Team2 = "PSV",
Date = DateTime.UtcNow
};
Game g3 = new Game()
{
Id = 3,
Team1 = "Ajax",
Team2 = "Feyenoord",
Date = DateTime.UtcNow
};
dbContext.Games.Add(g1);
dbContext.Games.Add(g2);
dbContext.Games.Add(g3);
await dbContext.SaveChangesAsync();
}
var gameRepository = new GameRepository(dbContext);
_gameService2 = new GameService(gameRepository);
}
When running the test I am running into the error: "The instance of entity type 'Game' cannot be tracked because another instance with the key value '{Id: 2}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
For comparing purposes, here is the adding function that DOES work:
[TestMethod()]
public async Task AddGameAsyncTest()
{
//Arrange
await CreateDb();
GameDto game = new GameDto()
{
Team1 = "Juve",
Team2 = "Real",
Date = DateTime.UtcNow
};
//Act
var id = await _gameService2.AddGameAsync(game);
var expected = await _gameService2.GetGameAsync(id);
//Assert
Assert.AreEqual("Juve", expected.Team1);
}
I have tried several things that answers to similar questions suggested but none of them work for me. I do have my repository en service scoped in Startup.cs, and detaching the entities in the createdb() function als doesn't seem to work. Does anyone have any other idea?
I think it's come from your UpdateGameAsync method. If you call EF Update inside, as the documentation says, you try to track the entity
Begins tracking the given entity in the Microsoft.EntityFrameworkCore.EntityState.Modified
But, i think your entity is already tracked, because i feel like you use the same dbContext in CreateDb, and this line begin the tracking of your entity:
dbContext.Games.Add(g2);
You can check if your entity is tracked in your repository:
_context.Games.Local.Any(g => g.Id == entity.Id)
If it's already tracked, you don't need to call EF Update().
By the way, if your real case do not use the same DbContext for data initialization and data read, i recommand to use another context to be sure that's work as you want.
I'm a newbie in integration tests and I'm looking for some explanation and advice about workaround of my issue:
I'm using TransactionScope in my tests to keep database clean and creating new TransactionScope before each test and dispose it after each test:
[SetUp]
public void Init()
{
this.scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted },
TransactionScopeAsyncFlowOption.Enabled);
this.context = new SportsPerformanceDbContext();
this.questRepo = new QuestionRepository(this.context);
}
[TearDown]
public void CleanAll()
{
this.context.Dispose();
this.scope.Dispose();
}
Everything works ok when I run one test class. But when I run at least two test classes, I'm facing a problem: in this test (see below) lasrQuestionId equals the last question id from database - that's ok, but actualResultId equals the Id_of_the_last_added_question_in_tests_with_transaction_scope + 1:
[Test]
public async void AddAsyncTest()
{
// Arrange
var questionModel = new QuestionModel
{
//some properties
};
Question lastQuestion = this.GetLastQuestion();
var lastQuestionId = lastQuestion?.Id ?? 0;
// Act
var addResult = await this.questRepo.AddAsync(questionModel);
var actualResult = addResult.Value;
// Assert
Assert.AreEqual(lastQuestionId + 1, actualResult.Id);
// some other assertions
}
So I have the following, e.g., lastQuestionId is 5 (5 questions in Database), but actualResult Id is 16 (because I've previously added some questions in other tests)... I assume that there is a problem with my context or scope.dispose(). I don't know where is a problem, could you explain what am I doing wrong here? Thanks in advance!
this.GetLastQuestion() code is below:
private Question GetLastQuestion()
{
using (var ctx = new SportsPerformanceDbContext())
{
return ctx.Question
.OrderByDescending(q => q.Id)
.FirstOrDefault();
}
}
This is how SQL Server engine works:
For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated.
I have created a SignalR hub and I want to unit test this hub. I have created multiple tests already, and when I fire them one by one they all succeed. But when I run them all at once a test is failing. The reason for failing is that this test is using a hub method that checks if some List has at least one value. The list should be empty in this test, but because other tests are running the list is filled by them.
How can I run this tests so each have their own instance of the hub?
As you can see below in the TryConnect() method it checks if the List ConnectedUsers is set and if there are more then MaxConnectedUsers in that list. In the test that is failing the list shouldn't be set and filled... but it is, because of other tests that are running.
How can I correctly test this?
For each test I create a mocked hub:
[TestMethod]
public void InitChat_Available()
{
HubWrapper hub = new HubWrapper();
hub.TryConnect();
Assert.AreEqual(1, hub.callerResponse.Count);
Assert.IsFalse((bool)hub.callerResponse[0]);
}
Init Method (Called in the HubWrapper constructor):
public void Init(string[] clientIds)
{
var connectionId = Guid.NewGuid().ToString();
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
var groupManagerMock = new Mock<IGroupManager>();
Clients = mockClients.Object;
caller.finishedInit = new Action<bool>((isAvailable) => callerResponse.Add(isAvailable));
mockClients.Setup(m => m.Caller).Returns((ExpandoObject)caller);
Groups = groupManagerMock.Object;
Dictionary<string, object> environment = new Dictionary<string, object>();
environment.Add("server.RemoteIpAddress", "127.0.0.10");
Context = new HubCallerContext(request: new ServerRequest(environment), connectionId: connectionId);
}
Hub TryConnect():
public void InitChat(string clientId)
{
bool isChatAvailable = (ConnectedUsers != null && ConnectedUsers.Count < MaxConnectedUsers);
Clients.Caller.finishedInit(isChatAvailable);
}
I've removed some (for this issue) unnecessary code.