Clean tests and Improve test redability to avoid problems between mocks - spring

Imagine the situation where I have several integration tests that validate the successful processing of a Product, under different conditions.
This Product has Id, Name, and Description. The description only accepts clear text, but I want to start accepting some HTML elements as well - Needless to say that I am considering XSS attacks
To validate the HTML tags I'm using JSoup which allows me to define only the HTML tags I want to allow.
This will be an example of the code I will use to mock the product, bearing in mind that for each case I need a setDescription(description) with different values:
private Product buildProduct() {
Product product = new Product();
product.setName("ProductName");
product.setDescription("<p>Description</p>")
}
That said, I will have to mock the product in three different ways:
Product mockup with description without HTML tags
Product mockup with description with valid HTML tags
Product mockup with description with invalid HTML tags
I thought of creating a buildProduct() for each case, but this leads to code duplication.
I thought about splitting this into three different and isolated tests, but it doesn't seem to make sense to create several different tests for similar situations.
What will be the cleanest way to do this, bearing in mind that the objective is not only to validate the tests but also to make the test as clean and readable as possible?

Given that you are using Java, there is no nice way to do this. The reason is that there is no nice way to copy an object and change one or more properties, just like you can do in Kotlin (check here for further details).
Having said that you have two options.
The first one is creating a utility class that actually creates all these different objects. Yes, code would be duplicated but at least this ugliness would be hidden in a very specific file.
public class TestObjects {
public static Product productWithDescriptionContainingNoHtmlTags() {
Product product = new Product();
product.setName("ProductName");
product.setDescription("No HTML Tags");
return product;
}
public static Product productWithDescriptionContainingValidHtmlTags() {
Product product = new Product();
product.setName("ProductName");
product.setDescription("<p>Description</p>")
return product;
}
public static Product productWithDescriptionContainingInvalidHtmlTags() {
Product product = new Product();
product.setName("ProductName");
product.setDescription("<invalid>Description</invalid>")
return product;
}
}
Then in your test class, you would only need to use these methods where suited.
The second one could be adding a constructor to your Product class that receives both name and description:
public class Product {
private String name;
private String description;
public Product() { }
public Product(String name, String description) {
this.name = name;
this.description = description;
}
}
Then in your test class, you could do something like this to create such objects:
private Product productWithDescriptionContainingNoHtmlTags =
new Product("ProductName", "No HTML Tags");
private Product productWithDescriptionContainingValidHtmlTags =
new Product("ProductName", "<p>Description</p>");
private Product productWithDescriptionContainingInvalidHtmlTags =
new Product("ProductName", "<invalid>Description</invalid>");
As a side note, one nice trick when you need to test some different scenarios without creating multiple tests is using Parameterized tests. Something along the following lines:
private static Stream<Arguments> products() {
return Stream.of(
Arguments.of(new Product("ProductName", "No HTML Tags"), true),
Arguments.of(new Product("ProductName", "<p>Description</p>"), true),
Arguments.of(new Product("ProductName", "<invalid>Description</invalid>"), false)
);
}
#ParameterizedTest
#MethodSource("products")
void assertProductsValidity(Product product, boolean isValid) {
// Your test here
}
Check https://www.baeldung.com/parameterized-tests-junit-5#6-method for more details.

Related

How to test repository without entity setters

I could use a constructor, but I want another way considering the case with many parameters.
Also, I don't want to change private fields to public.
please help me
#Test
#DisplayName("save the post")
void savePost() {
// Arrange
String title = "title...";
String contents = "contents...";
Post post = new Post(title, contents);
// Act
Post savedPost = postRepository.save(post);
...

Wicket - How to reload/refresh reusable components correctly?

I have a java class:
public Task {
private int id;
private Company sender;
private Company receiver;
//Getter and Setter
...
}
As you can see, I have 2 other custom classes in the task class. And a company has for example Adress and Directory.
I have a CompanyPanel which will reusable be used on the Page. Here is some code from the panel.
public class CompanyPanel extends Panel {
protected List<Company> companies;
public CompanyPanel(String id, IModel<Company> model) {
super(id,new CompoundPropertyModel<Company>(model));
companies = new ArrayList<Company>();
Company company_1 = new Company();
//Setting default predefined values for the company, so I can select it from the dropdown and to set fields automatically
company_1.setFtpAdress("adress1.com");
company_1.setFtpDir("/MusterDir/");
companies.add(company_1);
//SAME for another company
...
companies.add(comany_2);
...
final DropDownChoice<Company> companyList = new DropDownChoice<Company>("companies", model,
new LoadableDetachableModel<List<Company>>() {
#Override
protected List<Company> load() {
return companies;
}
}){
protected boolean wantOnSelectionChangedNotifications() {
return true;
}
};
add(companyList);
final TextField<String> ftpAdress = new TextField<String>("ftpAdress");
ftpAdress.setOutputMarkupId(true);
add(ftpAdress);
final TextField<String> ftpDir = new TextField<String>("ftpDir");
ftpDir.setOutputMarkupId(true);
add(ftpDir);
//added Ajax to dropdown to update textfields automatically, based on selection of dropdown
companyList.add(new AjaxFormComponentUpdatingBehavior("onchange")
{
#Override
protected void onUpdate(AjaxRequestTarget target)
{
target.add(ftpAdress);
target.add(ftpDir);
}
});
}
}
In the Page I use reuseable CompanyPanels.
...
CompanyPanel senderPanel = new CompanyPanel("senderPanel", new PropertyModel(task,"sender"));
senderPanel.setOutputMarkupId(true);
form.add(senderPanel);
CompanyPanel receiverPanel = new CompanyPanel("receiverPanel", new PropertyModel(task,"receiver"));
receiverPanel.setOutputMarkupId(true);
form.add(receiverPanel);
...
When I submit the form I do:
public void onSubmit(AjaxRequestTarget target, Form<?> form) {
//doSomething
target.add(senderPanel);
target.add(receiverPanel);
}
The problem: The company panel is not being rerendered. And I don't really know why.
Workflow:
I select a company from the dropdown panel
The TextFields(which are inside the companyPanel) will be set correctly, based on the dropdown
I modify a textField (which belongs to a company)
I submit the form
I change the company from the dropdown list
I change back to the first company -> PROBLEM: the modified textfields displays still the modified text inside. It was not reseted to the default values.
Any help very appreciated.
Of course they will display the modified values. You create a list of companies in the CompanyPanel constructor. When you modify a company's data, the object is modified inside that list.
A quick way to fix this would be to replace the CompanyPanel panel with a new instance of CompanyPanel in your onSubmit method. That would recreate the list of companies with your default values. You would of course lose the modified values.
Another possibly better fix is to move the companies list creation into the loadabledetachablemodel:
final DropDownChoice<Company> companyList = new DropDownChoice<Company>("companies", model,
new LoadableDetachableModel<List<Company>>() {
#Override
protected List<Company> load() {
List<Company>companies = new ArrayList<Company>();
Company company_1 = new Company();
//Setting default predefined values for the company, so I can select it from the dropdown and to set fields automatically
company_1.setFtpAdress("adress1.com");
company_1.setFtpDir("/MusterDir/");
companies.add(company_1);
//SAME for another company
...
companies.add(comany_2);
...
return companies;
}
This way the list of companies is recreated on every request with the default values.
Make sure you implement a proper equals() and hashCode() method in Company though for DropDownChoice to show the proper selected element though - because in this scenario the object in your model and the objects in the list may never be ==.
You have to provide more code. If you submit the correctly so that the model changes try:
senderPanel.modelChanged();
receiverPanel.modelChanged();
target.add(senderPanel);
target.add(receiverPanel);

Checkboxfor not binding model

So I'm new to MVC and ASP.Net. I've been reading through the posts on here all afternoon and I just can't get this to work. I have tried this, this, and this and I just don't know what I'm doing wrong.
Condition Class
public class conditions
{
public string condName;
public bool condValue;
public conditions(string s)
{
condName = s;
}
}
Risk History Class
public class riskHistory
{
public List<conditions> results;
public riskHistory()
{
results = new List<conditions>();
results.Add(new conditions("History of Marfans Syndrome"));
results.Add(new conditions("Family history of aortic disease"));
results.Add(new conditions("Recent aortic manipulation"));
results.Add(new conditions("Known thorasic aorta aneurysm"));
}
}
Now there is a riskPain and riskFeatures class defined the same way. I needed to split this into 3 because each has to be handled differently. What I display over each class unique in the view.
View Model defined as
public class adViewModel
{
public riskFeatures rF;
public riskPain rP;
public riskHistory rH;
public adViewModel()
{
rF = new riskFeatures();
rH = new riskHistory();
rP = new riskPain();
}
}
And then I have this code in the view. It's strongly typed to adViewModel.
Does the patient have any of the following history?
#for (int x =0; x<Model.rH.results.Count; x++)
{
string n = Model.rH.results[x].condName.ToString();
<b>#Html.Label(n)</b> #Html.CheckBoxFor(m => m.rH.results[x].condValue)
}
Does the patient have any of the following features to their pain?
// continue on to the other classes
The view displays correctly but when I submit the form it doesn't bind (all condvalue's are false). If you are going to suggest an editor template then I have a second question. The question in the view code above the for loop "Does the patient have the following history?" that question has to be different for each set of checkboxes. How would that work?
Thanks a ton guys!
~Chad
So, using the Html helpers like CheckBoxFor inside for loops caused problems for me because I didn't have control over the ids of the input tags that were generated. Check the final markup that is rendered and see what the ids of the controls it is making are. Writing out the html for the controls isn't really that much longer then calling the helper method anyway.

How to sort results by column in associated table in Grails?

I have two domain classes with a simple one to many relationship (a Gift can have many GiftInstance), as below. I wish to retrieve all GiftInstances for a given user (and potentially other criteria), but sort the results based on the 'expired' attribute of Gift associated with each GiftInstance. How would I accomplish this? I would prefer to stay within the confines of Grails (dynamic finder, Criteria, etc) not have to execute raw SQL
class Gift {
String name;
String description;
Date expires;
}
class GiftInstance {
static belongsTo = [gift: Gift]
User user;
... other fields ...
}
you can do this via mapping in your domain class with two ways.
either set the sort and the order in Gift class like this:
class Gift {
String name;
String description;
Date expires;
static hasMany = [giftInstances: GiftInstance]
static mapping = {
giftInstances sort: "expired"
}
}
or in your GiftInstance domain class.
class GiftInstance {
static belongsTo = [gift: Gift]
User user
boolean expired
static mapping = {
sort: "expired"
}
}
yes, you can also set the order.
You can add a named query in GiftInstance
class GiftInstance {
static namedQueries = {
giftInstanceByUser { user ->
eq 'user', user
order 'gift.expires', 'asc'
}
}
}

Observer pattern on GWT

Hey there! I'm relatively new to both GWT and java programming (or OOP for that matter), so apologies for the beginner questions/mistakes in advance. I've been trying to create some kind of observer pattern, but the development mode console keeps dropping error messages and sadly, they're far from helpful.
So here's what I'm trying to achieve:
- I've got the model that consists of the class Country, and stores a value called Influence.
- The view is the class called CountryDisplay. It's a GWT widget that should always display the current influence of a given country.
public class Country {
private int influece;
private CountryDisplay display;
public Country() {
influence = 0;
}
public void setDisplay(CountryDisplay display) //...
public int getInfluence() //...
public void setInfluence(int value) {
influence = value;
display.update();
}
}
public class CountryDisplay {
private Country country;
public CountryDisplay (Country country) {
//GWT widget creating stuff
this.country = country;
}
public void update() {
//InfluenceCounter is a simple Label
InfluenceCounter.setText(Integer.toString(country.getInfluence()));
}
}
Then in the EntryPoint class I do something like this:
Country italy = new Country();
CountryDisplay italyDisplay = new CountryDisplay(italy);
italy.setDisplay(italyDisplay);
RootPanel.get("nameFieldContainer").add(italyDisplay);
italy.setInfluence(3);
The development console indicated that it had a problem with the line "display.update();" in class Country. My first guess was that the problem was that the display was not initiated, so I created an interface for it, and in the Country constructor I created an empty, new display, that would later be overwritten.
public Country() {
influence = 0;
display = new DisplayInterface() {
public void update() {}
}
}
But I had no luck this way either. I guess this kind of cross-referencing is not allowed? I mean that the view has the model as a variable and vice versa.
When calling a method on the view individually (like:
italy.setInfluence(3);
italyDisplay.displayTheCurrentValue();
) it works, so the problem is definitely in the observer logic.
If I understand correctly, your are trying to "bind" user interface elements (your view class CountryDisplay) to data (the model class Country). "Bind" in the sense that if you change the model data (for example, call italy.setInfluence(10)) then the view would automatically update itself to reflect the change. And if your view provided an editor, you want the "binding" also to work in the other direction.
There are several frameworks out there that achieve this, see for example the post Best data binding solution for GWT. I have used GWT Pectin and there is the GWT Editors framework (which I have not yet used myself as it is relatively new).
Looking at your code, I feel you might want to more clearly separate the model from the view: your model class (Country) should not know about the view class, that is, it should not store a reference to CountryDisplay.

Resources