Given the following example code:
Table<Person> tableToQuery = MethodThatGetsReferenceToPersonTable();
string searchType = "NonUser";
IQueryable<Person> queryResults = tableToQuery.Where(p =>
(p.IsMale == false) && // exclude male people
type.Equals("User", StringComparison.OrdinalIgnoreCase)
? p.User != null : p.User == null);
I am new to L2S, but do have some of experience with EntityFramework. I would not expect the above query to work correctly in an ORM like EF because of the type.Equals that gets invoked in the predicate's ternary expression. I would instead expect EF to throw an exception since it cannot convert that part of the predicate into a (SQL) store expression.
With L2S, it seems that the .Where is returning data, but it is not excluding items where p.Male == true when type == "NonUser. I have already fixed the code above to pull the type.Equals ternary out of the predicate and return correct results, and am now trying to write a test to assert the correct results. The problem I am running into is that the L2S code is actually behind an IRepository<Person> interface.
So the actual code looks more like this:
string searchType = "NonUser";
ICriteria<Person> query = new Query<Person>(p =>
(p.IsMale == false) && // exclude male people
type.Equals("User", StringComparison.OrdinalIgnoreCase)
? p.User != null : p.User == null);
IQueryable<Person> = _peopleRepository.FindByQuery(query)
...and the _peopleRepository implementation just passes the query.Predicate as an argument to the L2S Table<Person>.Where.
Questions:
Is it correct that the L2S Table<Person>.Where is not returning the correct results because of the ternary expression in the lambda predicate? I assume so since taking the ternary out an generating separate ICriteria<Person> objects depending on the value of searchType is yielding correct results. However I am not so certain if this is because L2S cannot convert the ternary into a store expression, or if it is caused by something else.
Since the method under test depends on a IRepository<Person> instance, how could I actually write a unit test around this? Mocking the IRepository<Person> in a unit test would not allow us to test the effects of the lambda on real underlying data. Creating a FakePersonRepository backed by some kind of IList<Person> would not reveal the actual defect either because the above lambda with the ternary expression returns expected results using linq-to-objects. Is there any way we could mock part of L2S so that we can possibly generate SQL using the lambda, and write assertions against that instead?
Is this just something we have to do with an integration test and an actual L2S context with connection string, and cannot properly unit test? My definition of "integration test" means "connect to an actual database running in a separate process from the test runner using some kind of connection string", whereas "unit test" means "do everything in the test runner process".
Is it correct that the L2S (...) is not returning the correct results
It does return correct results, but not the results you expect, because you read the query as a human being and not as a compiler. The latter reads this:
tableToQuery.Where(p =>
((p.IsMale == false) && type.Equals("User", StringComparison.OrdinalIgnoreCase))
? p.User != null
: p.User == null);
We humans tend to read this:
tableToQuery.Where(p => (p.IsMale == false) &&
(type.Equals("User", StringComparison.OrdinalIgnoreCase)
? p.User != null
: p.User == null));
2./3. how could I actually write a unit test around this?
Mocking and unit testing is virtually impossible. I stick to integration tests when I want to test the behavior of a data layer. For Entity Framework I collected some reasons for integration tests here. Much of it applies too LINQ-to-SQL as well.
Related
I have one doubt about How to name test method, becuase I Lastly have doing unit test to a big project, and based in my experience I think that I am set name test method bad or worse, Here an example about my code.
public function notificationApproved(Request $request, User $user) {
$user = $user->getId();
$request = $request->getRequest();
$this->notification = $this->em->getRepository('AppBundle:Notification')->find(Notification::APPROVED);
$this->notificationCategory = NotificationCategory::APPROVED;
$this->notificationStatus = $this->em->getRepository('AppBundle:NotificationStatus')->find(1);
$this->reason = $reason;
//notification approvers project
foreach ($projectHasUserUnits as $keyData => $valueData) {
$projectUserUnitResponsability = $valueData->getProjectUserUnitResponsability()->last();
$responsability = $projectUserUnitResponsability->getResponsabilityProject();
if (is_null($projectUserUnitResponsability->getEndAt()) && ($responsability->getId() == Responsability::TYPE_RESPONSIBLE || $responsability->getId() == Responsability::TYPE_ACCOUNT_MANAGER || $responsability->getId() == Responsability::TYPE_PROJECT_MANAGER)) {
$user = $valueData->getUserHasUnit()->getUser();
if( !in_array($user,$this->users)){
$this->users[] = $user;
$description_label = 'notification_description_project_50';
$short_description = 'notification_content_project_50';
$notification = $this->generateNotification($Project, $user, $description_label,$short_description);
if ($notification) {
$this->notificationTrigger
->sendProjectNotification($user, $notification, $Project
, $notification->getDescription(), date('l m-d-y H:i a'));
}
}
}
}
}
it is a big method, I know, but don't worry for its logic, Just see the conditionals, and think how to name a method test like this, In this case, when itself come in all conditionals... maybe:
test_notificationApprovedWhenAllConditionsAreTrue
test_notificationApprovedWhenProjectHasUserUnitsIsBiggerThanZeroAndProjectUserUnitResponsabilityGetEndAtIsNotNullAndResponsabilityGetIdIsEqualToResponsabilityTYPERESPONSABILITYorResponsabilityGetIdIsEqualToResponsabilityTYPE_PROJECT_MANAGERanduserInArrayIsTrueAndNotificationIsTrue...
You only imagine read that !
However when it come only one conditional is easier that the above, !Of Course!, like this:
test_notificationApprovedWhenProjectHasUserUnitsIsBiggerThanZeroAndProjectUserUnitResponsabilityGetEndAtIsNotNullAndResponsabilityGetIdIsEqualToResponsabilityTYPERESPONSABILITYorResponsabilityGetIdIsEqualToResponsabilityTYPE_PROJECT_MANAGER
I've tried with comments inside test, but If a test fail, the idea would be guiding for its name (to fix the error rapidly)
So, do you have any idea ?
I have good experience with given<one-or-more-conditions>_action_result naming of test methods. Just a simple example for your case:
test_givenUserUnitGreaterThanZeroAndProjectIdEqualToOne_whenApproveNotification_thenNotificationIsSent()
I've simplified things for the sake of readability but you get the point. This nomenclature is taken from behavior-driven test frameworks. In languages that do not need the test_ you can save it.
Tests are really good indicators for code quality. Generally speaking if writing unit tests is easy then it's because the code to test is of "good" quality. You can follow some simple guidelines:
List every precondition exactly as it is necessary for the use case
Exactly one action. If not make multiple tests.
Exactly one result. If not extract methods and test them separately.
If method names get too long because of many preconditions then it might be time to extract a new class because this one has too many responsibilities
A bit of advice: if you'd add a test annotation, you would not need to prefix method names with test.
/**
* #test
*/
function givenThis_producesThat () {
I am using Hangfire for scheduling jobs. My code looks like this:
backgroundJob.Schedule<IDayBeforeRequestConfirmationMessageSender>(x => x.SendSms(requestIds, itinerary.Id), itinerary.Date.AddDays(-1));
my unit test (using MOQ), looks like this:
backgroundJobClient.Verify(x =>
x.Create(It.Is<Job>(job =>
job.Method.Name == nameof(IDayOfRequestConfirmationMessageSender.SendSms) &&
job.Args[0] == requestIds &&
(int)job.Args[1] == itinerary.Id),
It.Is<ScheduledState>(ss => ss.EnqueueAt == itinerary.Date.AddDays(-1))), Times.Once);
this is the failure I'm getting:
"Expected invocation on the mock once, but was 0 times: x => x.Create(It.Is(job => (job.Method.Name == "SendSms" && job.Args[0] == ) && (Int32)job.Args[1] == 1), It.Is(ss => ss.EnqueueAt == 10/24/2016 12:00:00 PM))"
If I change the It.Is<ScheduledState>(...) call to It.IsAny<ScheduledState>(), the unit test passes.
I'm using the same code to set up the date in the code I'm testing with the date I'm setting up to expect in the test, and they're the exact same value.
So, does anyone know what Hangfire is really doing behind the scenes? After looking at the source code, I don't think I want to use ss.ScheduledDate, b/c that name is misleading... that captures the datetime the job was added to Hangfire. A scheduled job (like an enqueued job) ends up being added to the EnqueueAt property...
has anyone had this problem testing the IBackgroundJobClient's
Schedule method before?
Okay, so ignore this question. It actually works. Sorry about that.
I have the following piece of code inside a service class named OrderService in Groovy on Grails. I want to make a unit test for this class. User and Order are domain classed. A user has many orders.
boolean testfun(long userId, lond orderId){
User user = User.findByUserId(userId)
if(user == null)return false
Order order = Order.findByUserAndId(user, orderId)
if(order == null)return false
return true
}
The unit test that I am trying to write is the following (using Spock):
#TestFor(OrderService)
#Mock([User, Order])
class OrderServiceSpec extends Specification{
def "test funtest"() {
User user = new User(2)
Order order = new Order()
order.metaClass.id = 3// I want to assign the id of the order in domain
order.save()
user.addToOrders(order)
user.save()
expect:
service.testfun(2,3) == true
}
}
However this test fails because the order is null. Can anyone help me?
Another question is: is this test a unit test? or should I write an integration test in grails?
It depends on what you're actually trying to test, but this can be a unit test—I'd just recommend modifying it a little bit to isolate only the service method that you're interested in testing. You're not looking to test the domain classes at all, so it's best to mock/stub the behavior that you need from them to test the service functionality.
A good way to do this is with Spock's support for interaction based testing via mock objects. Basically we specify that when the service's testfun() method is called, we expect User.findById() to be called once and Order.findByUserAndId() to be called once as well. Spock then allows us to stub out each method call so that we specify what we want the method to return. When we run the test, the stub will be used, not the actual GORM method.
Some complexity lies with stubbing out static methods (like GORM methods), but you can use a GroovySpy to get the job done.
Also, I'm assuming you meant to use User.findById() instead of User.findByUserId()?
Something along these lines should work for you:
def "test funtest"() {
setup:
// Global so that it replaces all instances/references of the
// mocked type for the duration of the feature method.
GroovySpy(User, global: true)
GroovySpy(Order, global: true)
when:
def result = service.testfun(2,3)
then:
// No need to return real objects, so use a mock
1 * User.findById(2) >> Mock(User)
1 * Order.findByUserAndId(_ as User, 3) >> Mock(Order)
result == true
when:
result = service.testfun(2,3)
then:
1 * User.findById(2) >> null
result == false
}
Note that we've isolated the service method. Any collaborating objects (User and Order) are only being interacted with via stubs and we can test the functionality of the service method without worrying about GORM at all.
I am trying to test some methods within a Domain object, the code seems to execute (based on log) but the assertions fail.
The code being tested (extendDates), is working, I am just adding the unit tests now.
I assume I am doing something wrong in the mocking.The following is a simplified version of my code. Based on the log output, the assertion should pass.
class EventDate{
Date startDate
Date endDate
belongsTo = [Appointments owner]
static constraints = {
endDate(nullable:true, blank:false)
startDate(nullable:false, blank:false)
}
}
class Appointments {
hasMany = [ eventDates: EventDate]
belongsTo = [ customer: Customer ]
def extendDates(start,end){
//some logic on eventDates...
EventDate(startDate:start,endDate:end, owner:this).save(flush:true,failOnError:true);
}
}
#TestFor(Appointments)
#Mock([EventDate])
class AppointmentsTests {
void testDateExtend(){
assertTrue domain != null
assertTrue domain instanceof Appointments
//Log indicates the correct execution and creation of event
domain.extendDates(new Date(),null)
//following returns NullPointerException
assertTrue domain.eventDates.size() == 1
}
}
In your example you test for
if (create_new)
The variable "create_new" is never set and would therefore test false using groovy truth logic, thus never executing the if statement.
The if statement never adds anything to the "eventDates" property of Appointments, this would also mean that the assertion would fail.
I'm thinking your example is incomplete and therefore cannot help you until you expand it.
Yes, you will get NullPointerException in the assert condition. The reason being that, you are creating instance of EventDate in the extendDates method but you are not really adding it to the eventDates list in Appointments domain.
So, you have to modify your that method something like:
// Initialize with empty array to avoid NPE
List<EventDate> eventDates = []
static hasMany = [ eventDates: EventDate]
def extendDates(start, end) {
EventDate instance = EventDate(start, end).save()
// And add it to the list of events
this.addToEventDates(instance)
this.save(flush: true)
}
Now, your test case should work your assert condition.
(Also, looks like you have not added nullable constraint in end but passing the null value while creating instance of EventDate, may be not included in sample code)
Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.